You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/12/28 03:12:28 UTC
svn commit: r1053242 - in /tapestry/tapestry5/trunk/tapestry-func/src:
main/java/org/apache/tapestry5/func/ test/java/org/apache/tapestry5/func/
Author: hlship
Date: Tue Dec 28 02:12:27 2010
New Revision: 1053242
URL: http://svn.apache.org/viewvc?rev=1053242&view=rev
Log:
TAP5-1390: Functional programming improvements
Squashed commit of the following:
commit cb241ca26a621f763cca45998edc6b2bec4c8bdc
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 18:05:28 2010 -0800
Add quick way to remove nulls from a flow
commit c0fd7b0d406ab21c563067a951995a2538567acc
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 17:55:26 2010 -0800
Simplify part of ZippedFlowImpl
commit a443fac51ac1a41982d0a1f7617576793f945ff5
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 17:54:10 2010 -0800
Add method to map the tuples of a ZippedFlow to tuples of a new ZippedFlow, and extract the firsts and seconds of the tuples into flows
commit 1e43c42d263008202ca692c7d5e9f8c0c7173c11
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 09:48:05 2010 -0800
Move reduce() from Flow to FlowOperations
commit 2dca8c17f4689a216669dd9c63dfba8fe101a659
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 09:41:03 2010 -0800
Test ability to concat to a ZippedFlow
commit ce889e0e5584fb855620a69619f4a36f161b6217
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 09:40:51 2010 -0800
Describe how the values from the two flows are combined in the Tuple
commit 776b3042af34ddf81060279b5b8936fa95a4d86c
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Mon Dec 27 09:40:34 2010 -0800
Move TupleTests to correct package
commit 9bc84c2bad9b5fafa1be082638235f5c7f87d2da
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Sun Dec 26 10:11:25 2010 -0800
Fill in more tests
commit d2c9a713d65b5e0d5baa82faaaaf4317d0078690
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Sun Dec 26 10:11:14 2010 -0800
Add missing copyright
commit 631c091fd498d6c53df9f7bef8b7959c397bcfd6
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Sun Dec 26 09:21:29 2010 -0800
Create a distinct interface for ZippedFlows and factor out common operations into FlowOperations
commit f8b08b4b8fe26e5b1372cb36b08401011d32e8bd
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Sat Dec 25 14:35:23 2010 -0800
Introduce a basic ZippedFlow interface and implementation
commit 2032c175c255a3862f7ef5a9c350153fb57d11db
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Thu Dec 23 17:19:32 2010 -0800
Flesh out the Javadoc on a number of methods
commit 265a6adb83b4279380e864c1fbf218bfc23233c9
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Thu Dec 23 17:19:14 2010 -0800
Add some additional tests on Tuple
commit ec9515193e44ce6a202cd997a05785a271ce3756
Author: Howard M. Lewis Ship <hl...@gmail.com>
Date: Thu Dec 23 11:22:18 2010 -0800
Add a Tuple class and Flow.zipWith() to zip two flows together into a flow of Tuples
Added:
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/FlowOperations.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZip.java
- copied, changed from r1052930, tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZipValue.java
- copied, changed from r1052930, tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Tuple.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlow.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlowImpl.java
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TupleTests.java
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/ZippedFlowTests.java
Modified:
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/AbstractFlow.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/EmptyFlow.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/F.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Flow.java
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TakeDropTests.java
Modified: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/AbstractFlow.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/AbstractFlow.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/AbstractFlow.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/AbstractFlow.java Tue Dec 28 02:12:27 2010
@@ -24,8 +24,8 @@ import java.util.List;
import java.util.Set;
/**
- * Abstract base class for implementations of {@link Flow}. Subclasses typically override some methods
- * for either efficiency, or for the concern they embrace.
+ * Abstract base class for implementations of {@link Flow}. Subclasses typically override some
+ * methods for either efficiency, or for the concern they embrace.
*
* @since 5.2.0
*/
@@ -246,4 +246,18 @@ abstract class AbstractFlow<T> implement
return Collections.unmodifiableSet(set);
}
+ public <X> ZippedFlow<T, X> zipWith(Flow<X> otherFlow)
+ {
+ assert otherFlow != null;
+
+ Flow<Tuple<T, X>> tupleFlow = F.lazy(new LazyZip<T, X>(this, otherFlow));
+
+ return ZippedFlowImpl.create(tupleFlow);
+ }
+
+ public Flow<T> removeNulls()
+ {
+ return remove(F.isNull());
+ }
+
}
Modified: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/EmptyFlow.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/EmptyFlow.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/EmptyFlow.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/EmptyFlow.java Tue Dec 28 02:12:27 2010
@@ -127,5 +127,9 @@ class EmptyFlow<T> extends AbstractFlow<
return Collections.emptySet();
}
-
+ @Override
+ public Flow<T> removeNulls()
+ {
+ return this;
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/F.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/F.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/F.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/F.java Tue Dec 28 02:12:27 2010
@@ -18,15 +18,17 @@ import java.util.Collection;
import java.util.Iterator;
/**
- * Functional operations on collections with generics support. The core interface is {@link Flow} to which operations
+ * Functional operations on collections with generics support. The core interface is {@link Flow} to
+ * which operations
* and transformations
- * (in terms of {@link Predicate}s, {@link Mapper}s and {@link Reducer}s) to create new Flows. Flows are initially
+ * (in terms of {@link Predicate}s, {@link Mapper}s and {@link Reducer}s) to create new Flows. Flows
+ * are initially
* created
* using {@link #flow(Collection)} and {@link #flow(Object...)}.
* <p>
- * F will be used a bit, thus it has a short name (for those who don't like static imports). It provides a base set of
- * Predicate, Mapper and Reducer factories. A good development pattern for applications is to provide a similar,
- * application-specific, set of such factories.
+ * F will be used a bit, thus it has a short name (for those who don't like static imports). It
+ * provides a base set of Predicate, Mapper and Reducer factories. A good development pattern for
+ * applications is to provide a similar, application-specific, set of such factories.
*
* @since 5.2.0
*/
@@ -41,6 +43,9 @@ public class F
return (Flow<T>) EMPTY_FLOW;
}
+ /**
+ * A Predicate factory for equality against a specified value.
+ */
public static <T> Predicate<T> eql(final T value)
{
return new Predicate<T>()
@@ -52,6 +57,10 @@ public class F
};
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the Number equals the value.
+ */
public static Predicate<Number> eq(final long value)
{
return new Predicate<Number>()
@@ -63,11 +72,19 @@ public class F
};
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the Number does not equal the value.
+ */
public static Predicate<Number> neq(long value)
{
return eq(value).invert();
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the number is greater than the value.
+ */
public static Predicate<Number> gt(final long value)
{
return new Predicate<Number>()
@@ -79,21 +96,36 @@ public class F
};
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the Number is greater than or equal to the value.
+ */
public static Predicate<Number> gteq(long value)
{
return eq(value).or(gt(value));
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the Number is less than the value.
+ */
public static Predicate<Number> lt(long value)
{
return gteq(value).invert();
}
+ /**
+ * A Predicate factory for comparison of a Number against a fixed value; true
+ * if the Number is less than or equal to the value.
+ */
public static Predicate<Number> lteq(long value)
{
return gt(value).invert();
}
+ /**
+ * A Predicate factory; returns true if the value from the Flow is null.
+ */
public static <T> Predicate<T> isNull()
{
return new Predicate<T>()
@@ -105,6 +137,9 @@ public class F
};
}
+ /**
+ * A Predicate factory; returns true if the value from the Flow is not null.
+ */
public static <T> Predicate<T> notNull()
{
Predicate<T> isNull = isNull();
@@ -112,6 +147,10 @@ public class F
return isNull.invert();
}
+ /**
+ * A Mapper factory that gets the string value of the flow value using
+ * {@link String#valueOf(Object)}.
+ */
public static <T> Mapper<T, String> stringValueOf()
{
return new Mapper<T, String>()
@@ -123,7 +162,10 @@ public class F
};
}
- /** Returns a Mapper that ignores its input value and always returns a predetermined result. */
+ /**
+ * A Mapper factory; the returned Mapper ignores its input value and always returns a
+ * predetermined result.
+ */
public static <S, T> Mapper<S, T> always(final T fixedResult)
{
return new Mapper<S, T>()
@@ -136,8 +178,8 @@ public class F
}
/**
- * Mapper factory that combines a Predicate with two {@link Mapper}s; evaluating the predicate selects one of the
- * two mappers.
+ * A Mapper factory that combines a Predicate with two {@link Mapper}s; evaluating the predicate
+ * selects one of the two mappers.
*
* @param predicate
* evaluated to selected a coercion
@@ -165,7 +207,8 @@ public class F
}
/**
- * Override of {@link #select(Predicate, Mapper, Mapper)} where rejected values are replaced with null.
+ * Override of {@link #select(Predicate, Mapper, Mapper)} where rejected values are replaced
+ * with null.
*/
public static <S, T> Mapper<S, T> select(Predicate<? super S> predicate, Mapper<S, T> ifAccepted)
{
@@ -173,7 +216,8 @@ public class F
}
/**
- * Override of {@link #select(Predicate, Mapper)} where rejected values are replaced with a fixed value.
+ * Override of {@link #select(Predicate, Mapper)} where rejected values are replaced with a
+ * fixed value.
*/
public static <S, T> Mapper<S, T> select(Predicate<? super S> predicate, Mapper<S, T> ifAccepted, T ifRejectedValue)
{
@@ -182,7 +226,7 @@ public class F
return select(predicate, ifAccepted, rejectedMapper);
}
- /** The identity mapper simply returns the input unchanged. */
+ /** A Mapper factory; the Mapper returns the the flow value unchanged. */
public static <S> Mapper<S, S> identity()
{
return new Mapper<S, S>()
@@ -208,6 +252,9 @@ public class F
};
}
+ /**
+ * A Reducer that operates on a Flow of Integers and is used to sum the values.
+ */
public static Reducer<Integer, Integer> SUM_INTS = new Reducer<Integer, Integer>()
{
public Integer reduce(Integer accumulator, Integer value)
@@ -216,6 +263,10 @@ public class F
};
};
+ /**
+ * A two-input Mapper used to add the values from two Flows of Integers into a Flow of Integer
+ * sums.
+ */
public static Mapper2<Integer, Integer, Integer> ADD_INTS = new Mapper2<Integer, Integer, Integer>()
{
public Integer map(Integer first, Integer second)
@@ -252,8 +303,9 @@ public class F
/**
* Creates a lazy Flow from the {@link Iterator} obtained from the iterable. The Flow
- * will be threadsafe as long as the iterable yields a new Iterator on each invocation <em>and</em> the underlying
- * iterable object is not modified while the Flow is evaluating. In other words, not extremely threadsafe.
+ * will be threadsafe as long as the iterable yields a new Iterator on each invocation
+ * <em>and</em> the underlying iterable object is not modified while the Flow is evaluating.
+ * In other words, not extremely threadsafe.
*/
public static <T> Flow<T> flow(Iterable<T> iterable)
{
@@ -306,7 +358,8 @@ public class F
/**
* Creates a lazy, infinte Flow consisting of the initial value, then the result of passing
- * the initial value through the Mapper, and so forth, which each step value passed through the mapper
+ * the initial value through the Mapper, and so forth, which each step value passed through the
+ * mapper
* to form the next step value.
*/
public static <T> Flow<T> iterate(final T initial, final Mapper<T, T> mapper)
@@ -323,6 +376,9 @@ public class F
});
}
+ /**
+ * A Worker factory; the returnedWorker adds the values to a provided collection.
+ */
public static <T> Worker<T> addToCollection(final Collection<T> coll)
{
return new Worker<T>()
Modified: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Flow.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Flow.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Flow.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Flow.java Tue Dec 28 02:12:27 2010
@@ -14,38 +14,39 @@
package org.apache.tapestry5.func;
-import java.util.Comparator;
import java.util.List;
-import java.util.Set;
/**
- * A Flow is a a functional interface for working with an ordered collection of values.
- * A given Flow contains only values of a particular type. Standard operations allow for
- * filtering the Flow, or appending values to the Flow. Since Flows are immutable, all operations
- * on Flows return new immutable Flows. Flows are thread safe (to the extent that the {@link Mapper}s, {@link Predicate}
- * s, {@link Worker}s and {@link Reducer}s applied to the Flow are).
- * Flows are <em>lazy</em>: filtering, mapping, and concatenating Flows will do so with no, or a minimum, of evaluation.
- * However, converting a Flow into a {@link List} will force a realization of the entire Flow.
+ * A flow is a a functional interface for working with an ordered collection of elements.
+ * A given Flow contains only elements of a particular type. Standard operations allow for
+ * filtering the flow, or appending elements to the Flow. Since flows are immutable, all operations
+ * on flows return new immutable flows. Flows are thread safe (to the extent that the {@link Mapper}
+ * , {@link Predicate}, {@link Worker} and {@link Reducer} objects applied to the flow are).
+ * Flows are <em>lazy</em>: filtering, mapping, and concatenating flows will do so with no, or a
+ * minimum, of evaluation. However, converting a Flow into a {@link List} (or other collection) will
+ * force a realization of the entire flow.
* <p>
- * In some cases, a Flow may be an infinite, lazily evaluated sequence. Operations that iterate over all values (such as
- * {@link #count()} or {@link #reduce(Reducer, Object)}) may become infinite loops.
+ * In some cases, a flow may be an infinite, lazily evaluated sequence. Operations that iterate over
+ * all elements (such as {@link #count()} or {@link #reduce(Reducer, Object)}) may become infinite
+ * loops.
* <p>
- * Using Flows allows for a very fluid interface.
+ * Using flows allows for a very fluid interface.
* <p>
- * Flows are initially created using {@link F#flow(java.util.Collection)} or {@link F#flow(Object...)}.
+ * Flows are initially created using {@link F#flow(java.util.Collection)}, {@link F#flow(Object...)}
+ * or {@link F#flow(Iterable)}.
*
* @since 5.2.0
* @see F#lazy(LazyFunction)
*/
-public interface Flow<T> extends Iterable<T>
+public interface Flow<T> extends FlowOperations<T, Flow<T>>
{
/** Maps a Flow into a new Flow with different type values. Mapping is a lazy operation. */
<X> Flow<X> map(Mapper<T, X> mapper);
/**
- * Combines two Flows using a two-parameter Mapper. Each value of
- * this Flow, and the corresponding value of the other flow are passed through the Mapper
- * to provide the values of the output Flow. The length of the result Flow is
+ * Combines two Flows using a two-parameter Mapper. Each element of
+ * this Flow, and the corresponding element of the other flow are passed through the Mapper
+ * to provide the elements of the output Flow. The length of the result Flow is
* the smaller of the lengths of the two input Flows. Mapping is a lazy operation.
*/
<X, Y> Flow<Y> map(Mapper2<T, X, Y> mapper, Flow<? extends X> flow);
@@ -57,66 +58,26 @@ public interface Flow<T> extends Iterabl
<X> Flow<X> mapcat(Mapper<T, Flow<X>> mapper);
/**
- * Filters values, keeping only values where the predicate is true, returning a new Flow with just
- * the retained values.
+ * Converts the Flow into an array of values (due to type erasure, you have to remind the Flow
+ * about the type).
*/
- Flow<T> filter(Predicate<? super T> predicate);
-
- /** Removes values where the predicate returns true, returning a new Flow with just the remaining values. */
- Flow<T> remove(Predicate<? super T> predicate);
-
- /**
- * Applies a Reducer to the values of the Flow. The Reducer is passed the initial value
- * and the first value from the Flow. The result is captured as the accumulator and passed
- * to the Reducer with the next value from the Flow, and so on. The final accumulator
- * value is returned. If the flow is empty, the initial value is returned.
- * <p>
- * Reducing is a non-lazy operation; it will fully realize the values of the Flow.
- */
- <A> A reduce(Reducer<A, T> reducer, A initial);
-
- /**
- * Applies the worker to each value in the Flow, then returns the flow for further behaviors.
- * <p>
- * Each is a non-lazy operation; it will fully realize the values of the Flow.
- */
- Flow<T> each(Worker<? super T> worker);
-
- /**
- * Converts the Flow into an unmodifiable list of values. This is a non-lazy operation that will fully realize
- * the values of the Flow.
- */
- List<T> toList();
+ T[] toArray(Class<T> type);
/**
- * Converts the Flow into an unmodifiable set of values. This is a non-lazy operation that will fully realize
- * the values of the Flow.
+ * Returns a new Flow with the other Flow's elements appended to this Flow's. This is a lazy
+ * operation.
*/
- Set<T> toSet();
+ Flow<T> concat(Flow<? extends T> other);
/**
- * Converts the Flow into an array of values (due to type erasure, you have to remind the Flow about the
- * type).
+ * Appends any number of type compatible values to the end of this Flow. This is a lazy
+ * operation.
*/
- T[] toArray(Class<T> type);
-
- /** Returns a new flow with the same elements but in reverse order. */
- Flow<T> reverse();
-
- /** Returns true if the Flow contains no values. This <em>may</em> realize the first value in the Flow. */
- boolean isEmpty();
-
- /** Returns a new Flow with the other Flow's elements appended to this Flow's. This is a lazy operation. */
- Flow<T> concat(Flow<? extends T> other);
-
- /** Returns a new Flow with the values in the list appended to this Flow. This is a lazy operation. */
- Flow<T> concat(List<? extends T> list);
-
- /** Appends any number of type compatible values to the end of this Flow. This is a lazy operation. */
<V extends T> Flow<T> append(V... values);
/**
- * Sorts this Flow, forming a new Flow. This is a non-lazy operation; it will fully realize the values of the Flow.
+ * Sorts this Flow, forming a new Flow. This is a non-lazy operation; it will fully realize the
+ * values of the Flow.
*
* @throws ClassCastException
* if type <T> does not extend {@link Comparable}
@@ -124,44 +85,17 @@ public interface Flow<T> extends Iterabl
Flow<T> sort();
/**
- * Sorts this Flow using the comparator, forming a new Flow. This is a non-lazy operation; it will fully realize the
- * values of the Flow.
- */
- Flow<T> sort(Comparator<? super T> comparator);
-
- /**
- * Returns the first value in the Flow. Returns null for empty flows, but remember that null is a valid
- * value within a flow, so use {@link #isEmpty() to determine if a flow is actually empty. The first value can be
- * realized without realizing the full Flow.
- */
- T first();
-
- /**
- * Returns a new Flow containing all but the first value in this Flow. If this Flow has only a single item,
- * or is empty, this will return an empty Flow.
- */
- Flow<T> rest();
-
- /**
- * Returns the number of values in the Flow. This forces the realization of much of the Flow (i.e., because
- * each value will need to be passed through any {@link Predicate}s).
- */
- int count();
-
- /**
- * Returns a new Flow containing just the first values from
- * this Flow.
- *
- * @param length
- * maximum number of values in the Flow
- */
- Flow<T> take(int length);
-
- /**
- * Returns a new Flow with the first values omitted.
+ * Zips this Flow together with another flow to form a Flow of {@link Tuple}s. The resulting
+ * flow is the length of the shorter of the two input flows.
+ * <p>
+ * The elements of this flow become the {@linkplain Tuple#first} value in each Tuple, the
+ * elements of the other flow become the {@linkplain Tuple#second} value in each Tuple.
*
- * @param length
- * number of values to drop
+ * @param <B>
+ * @param otherFlow
+ * contains values to match with values in this flow
+ * @return flow of tuples combining values from this flow with values form the other flow
+ * @since 5.3.0
*/
- Flow<T> drop(int length);
+ <X> ZippedFlow<T, X> zipWith(Flow<X> otherFlow);
}
Added: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/FlowOperations.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/FlowOperations.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/FlowOperations.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/FlowOperations.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,121 @@
+package org.apache.tapestry5.func;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @param <T>
+ * the type of data in the flow
+ * @param <FT>
+ * the type of flow (either Flow<T> or ZippedFlow<Tuple<T, ?>)
+ * @since 5.3.0
+ */
+public interface FlowOperations<T, FT> extends Iterable<T>
+{
+ /**
+ * Filters values, keeping only values where the predicate is true, returning a new Flow with
+ * just the retained values.
+ */
+ FT filter(Predicate<? super T> predicate);
+
+ /**
+ * Removes values where the predicate returns true, returning a new Flow with just the remaining
+ * values.
+ */
+ FT remove(Predicate<? super T> predicate);
+
+ /**
+ * Applies the worker to each element in the Flow, then returns the flow for further behaviors.
+ * <p>
+ * Each is a non-lazy operation; it will fully realize the values of the Flow.
+ */
+ FT each(Worker<? super T> worker);
+
+ /**
+ * Converts the Flow into an unmodifiable list of values. This is a non-lazy operation that will
+ * fully realize the values of the Flow.
+ */
+ List<T> toList();
+
+ /**
+ * Converts the Flow into an unmodifiable set of values. This is a non-lazy operation that will
+ * fully realize the values of the Flow.
+ */
+ Set<T> toSet();
+
+ /** Returns a new flow with the same elements but in reverse order. */
+ FT reverse();
+
+ /**
+ * Returns true if the Flow contains no values. This <em>may</em> realize the first value in the
+ * Flow.
+ */
+ boolean isEmpty();
+
+ /**
+ * Returns the first element in the Flow. Returns null for empty flows, but remember that null
+ * is a valid element within a flow, so use {@link #isEmpty() to determine if a flow is actually
+ * empty. The first element can be realized without realizing the full Flow.
+ */
+ T first();
+
+ /**
+ * Returns a new Flow containing all but the first element in this flow. If this flow has only a
+ * single element, or is empty, this will return an empty Flow.
+ */
+ FT rest();
+
+ /**
+ * Returns the number of values in this flow. This forces the realization of much of the flow
+ * (i.e., because each value will need to be passed through any {@link Predicate}s).
+ */
+ int count();
+
+ /**
+ * Sorts this flow using the comparator, forming a new flow. This is a non-lazy operation; it
+ * will fully realize the values of the Flow.
+ */
+ FT sort(Comparator<? super T> comparator);
+
+ /**
+ * Returns a new flow containing just the first values from
+ * this Flow.
+ *
+ * @param length
+ * maximum number of values in the Flow
+ */
+ FT take(int length);
+
+ /**
+ * Returns a new flow with the first values omitted.
+ *
+ * @param length
+ * number of values to drop
+ */
+ FT drop(int length);
+
+ /**
+ * Returns a new Flow with the values in the list appended to this Flow. This is a lazy
+ * operation.
+ */
+ FT concat(List<? extends T> list);
+
+ /**
+ * Applies a Reducer to the values of the Flow. The Reducer is passed the initial value
+ * and the first element from the Flow. The result is captured as the accumulator and passed
+ * to the Reducer with the next value from the Flow, and so on. The final accumulator
+ * value is returned. If the flow is empty, the initial value is returned.
+ * <p>
+ * Reducing is a non-lazy operation; it will fully realize the values of the Flow.
+ */
+ <A> A reduce(Reducer<A, T> reducer, A initial);
+
+ /**
+ * Removes null elements from the flow (null tuples from a ZippedFlow), leaving just the
+ * non-null elements. This is a lazy operation.
+ *
+ * @since 5.3.0
+ */
+ FT removeNulls();
+}
Copied: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZip.java (from r1052930, tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZip.java?p2=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZip.java&p1=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java&r1=1052930&r2=1053242&rev=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZip.java Tue Dec 28 02:12:27 2010
@@ -14,15 +14,25 @@
package org.apache.tapestry5.func;
-/**
- * A reducer takes an accumulator value and a single value from a collection and computes a new
- * accumulator value.
- * <A> type of accumulator
- * <T> type of collection value
- *
- * @since 5.2.0
- */
-public interface Reducer<A, T>
+class LazyZip<A, B> implements LazyFunction<Tuple<A, B>>
{
- A reduce(A accumulator, T value);
+ private final Flow<A> aFlow;
+
+ private final Flow<B> bFlow;
+
+ public LazyZip(Flow<A> aFlow, Flow<B> bFlow)
+ {
+ this.aFlow = aFlow;
+ this.bFlow = bFlow;
+ }
+
+ public LazyContinuation<Tuple<A, B>> next()
+ {
+ if (aFlow.isEmpty() || bFlow.isEmpty())
+ return null;
+
+ LazyZipValue<A, B> nextValue = new LazyZipValue<A, B>(aFlow, bFlow);
+
+ return new LazyContinuation<Tuple<A, B>>(nextValue, new LazyZip<A, B>(aFlow.rest(), bFlow.rest()));
+ }
}
Copied: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZipValue.java (from r1052930, tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZipValue.java?p2=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZipValue.java&p1=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java&r1=1052930&r2=1053242&rev=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyZipValue.java Tue Dec 28 02:12:27 2010
@@ -14,15 +14,20 @@
package org.apache.tapestry5.func;
-/**
- * A reducer takes an accumulator value and a single value from a collection and computes a new
- * accumulator value.
- * <A> type of accumulator
- * <T> type of collection value
- *
- * @since 5.2.0
- */
-public interface Reducer<A, T>
+public class LazyZipValue<A, B> implements LazyValue<Tuple<A, B>>
{
- A reduce(A accumulator, T value);
+ private final Flow<A> aFlow;
+
+ private final Flow<B> bFlow;
+
+ public LazyZipValue(Flow<A> aFlow, Flow<B> bFlow)
+ {
+ this.aFlow = aFlow;
+ this.bFlow = bFlow;
+ }
+
+ public Tuple<A, B> get()
+ {
+ return new Tuple<A, B>(aFlow.first(), bFlow.first());
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Reducer.java Tue Dec 28 02:12:27 2010
@@ -24,5 +24,9 @@ package org.apache.tapestry5.func;
*/
public interface Reducer<A, T>
{
+ /**
+ * Run a computation using the current value of the accumulator and a value (from a Flow),
+ * and return the new accumulator.
+ */
A reduce(A accumulator, T value);
}
Added: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Tuple.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Tuple.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Tuple.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/Tuple.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,104 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.apache.tapestry5.func;
+
+/**
+ * A Tuple holds two values of two different types.
+ *
+ * @param <A>
+ * @param <B>
+ * @since 5.3.0
+ */
+public class Tuple<A, B>
+{
+ public final A first;
+
+ public final B second;
+
+ public Tuple(A first, B second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public static <X, Y> Tuple<X, Y> create(X first, Y second)
+ {
+ return new Tuple<X, Y>(first, second);
+ }
+
+ /**
+ * Returns the values of the tuple, separated by commas, enclosed in parenthesis. Example:
+ * <code>("Ace", "Spades")</code>.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder("(");
+
+ builder.append(String.valueOf(first));
+ builder.append(", ");
+ builder.append(String.valueOf(second));
+
+ extendDescription(builder);
+
+ return builder.append(")").toString();
+ }
+
+ /**
+ * Overriden in subclasses to write additional values into the
+ * description.
+ *
+ * @param builder
+ */
+ protected void extendDescription(StringBuilder builder)
+ {
+ }
+
+ /** Utility for comparing two values, either of which may be null. */
+ static boolean isEqual(Object left, Object right)
+ {
+ return left == right || (left != null && left.equals(right));
+ }
+
+ /**
+ * Compares this Tuple to another object. Equality is defined by: other object is not null,
+ * is same class as this Tuple, and all values are themselves equal.
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == this)
+ return true;
+
+ if (obj == null || !(obj.getClass() == getClass()))
+ return false;
+
+ return isMatch(obj);
+ }
+
+ /**
+ * The heart of {@link #equals(Object)}; the other object is the same class as this object.
+ *
+ * @param other
+ * other tuple to compare
+ * @return true if all values stored in tuple match
+ */
+ protected boolean isMatch(Object other)
+ {
+ Tuple<?, ?> tuple = (Tuple<?, ?>) other;
+
+ return isEqual(first, tuple.first) && isEqual(second, tuple.second);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlow.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlow.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlow.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlow.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,51 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.apache.tapestry5.func;
+
+/**
+ * The result of the {@link Flow#zipWith(Flow)} method, a Flow of combined {@link Tuple} values
+ * (that can be deconstructed, eventually, using {@link #unzip()}). Each operation of {@link Flow}
+ * has a corresponding implementation here, on the Tuple values.
+ *
+ * @param <A>
+ * @param <B>
+ * @since 5.3.0
+ */
+public interface ZippedFlow<A, B> extends FlowOperations<Tuple<A, B>, ZippedFlow<A, B>>
+{
+ /**
+ * Mapping for zipped flows; a mapper is used to map tuples of this zipped flow into new tuples
+ * with a new type, forming the resulting zipped flow.
+ */
+ <X, Y> ZippedFlow<X, Y> mapTuples(Mapper<Tuple<A, B>, Tuple<X, Y>> mapper);
+
+ /**
+ * A ZippedFlow is a Flow of Tuples; this inverts that, splitting each Tuple into
+ * a Flow of values, then assembling the result as a Tuple of two values.
+ *
+ * @return two flows of unzipped Tuples
+ */
+ Tuple<Flow<A>, Flow<B>> unzip();
+
+ /**
+ * Returns a flow of the first values of the tuples of the zipped flow.
+ */
+ Flow<A> firsts();
+
+ /**
+ * Returns a flow of the second values of the tuples of the zipped flow.
+ */
+ Flow<B> seconds();
+}
Added: tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlowImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlowImpl.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlowImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/ZippedFlowImpl.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,171 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.apache.tapestry5.func;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The single implementation of {@link ZippedFlow}, that operates by wrapping around an ordinary
+ * {@link Flow} of the {@link Tuples} of the zipped flow. In the future, we may create an
+ * EmptyZippedFlow implementation as well.
+ *
+ * @param <A>
+ * type of first value in tuple
+ * @param <B>
+ * type of second value in tuple
+ */
+class ZippedFlowImpl<A, B> implements ZippedFlow<A, B>
+{
+ private final Flow<Tuple<A, B>> tupleFlow;
+
+ private ZippedFlowImpl(Flow<Tuple<A, B>> tupleFlow)
+ {
+ this.tupleFlow = tupleFlow;
+ }
+
+ public Tuple<Flow<A>, Flow<B>> unzip()
+ {
+ return Tuple.create(firsts(), seconds());
+ }
+
+ static <X, Y> ZippedFlow<X, Y> create(Flow<Tuple<X, Y>> wrappedTupleFlow)
+ {
+ assert wrappedTupleFlow != null;
+
+ return new ZippedFlowImpl<X, Y>(wrappedTupleFlow);
+ }
+
+ public ZippedFlow<A, B> filter(Predicate<? super Tuple<A, B>> predicate)
+ {
+ return create(tupleFlow.filter(predicate));
+ }
+
+ public ZippedFlow<A, B> remove(Predicate<? super Tuple<A, B>> predicate)
+ {
+ return create(tupleFlow.remove(predicate));
+ }
+
+ public ZippedFlow<A, B> each(Worker<? super Tuple<A, B>> worker)
+ {
+ tupleFlow.each(worker);
+
+ return this;
+ }
+
+ public List<Tuple<A, B>> toList()
+ {
+ return tupleFlow.toList();
+ }
+
+ public Set<Tuple<A, B>> toSet()
+ {
+ return tupleFlow.toSet();
+ }
+
+ public ZippedFlow<A, B> reverse()
+ {
+ return create(tupleFlow.reverse());
+ }
+
+ public boolean isEmpty()
+ {
+ return tupleFlow.isEmpty();
+ }
+
+ public Tuple<A, B> first()
+ {
+ return tupleFlow.first();
+ }
+
+ public ZippedFlow<A, B> rest()
+ {
+ return create(tupleFlow.rest());
+ }
+
+ public int count()
+ {
+ return tupleFlow.count();
+ }
+
+ public ZippedFlow<A, B> sort(Comparator<? super Tuple<A, B>> comparator)
+ {
+ return create(tupleFlow.sort(comparator));
+ }
+
+ public ZippedFlow<A, B> take(int length)
+ {
+ return create(tupleFlow.take(length));
+ }
+
+ public ZippedFlow<A, B> drop(int length)
+ {
+ return create(tupleFlow.drop(length));
+ }
+
+ public ZippedFlow<A, B> concat(List<? extends Tuple<A, B>> list)
+ {
+ return create(tupleFlow.concat(list));
+ }
+
+ public Iterator<Tuple<A, B>> iterator()
+ {
+ return tupleFlow.iterator();
+ }
+
+ public <O> O reduce(Reducer<O, Tuple<A, B>> reducer, O initial)
+ {
+ return tupleFlow.reduce(reducer, initial);
+ }
+
+ public <X, Y> ZippedFlow<X, Y> mapTuples(Mapper<Tuple<A, B>, Tuple<X, Y>> mapper)
+ {
+ return create(tupleFlow.map(mapper));
+ }
+
+ public Flow<A> firsts()
+ {
+ return tupleFlow.map(new Mapper<Tuple<A, B>, A>()
+ {
+
+ @Override
+ public A map(Tuple<A, B> value)
+ {
+ return value.first;
+ }
+ });
+ }
+
+ public ZippedFlow<A, B> removeNulls()
+ {
+ return create(tupleFlow.removeNulls());
+ }
+
+ public Flow<B> seconds()
+ {
+ return tupleFlow.map(new Mapper<Tuple<A, B>, B>()
+ {
+
+ @Override
+ public B map(Tuple<A, B> value)
+ {
+ return value.second;
+ }
+ });
+ }
+
+}
Modified: tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java Tue Dec 28 02:12:27 2010
@@ -546,7 +546,7 @@ public class FuncTest extends BaseFuncTe
Flow<String> flow = F.flow("Mary", "had", "a", "little", "lamb");
assertEquals(flow.filter(F.isNull()).count(), 0);
- assertEquals(flow.filter(F.notNull()).count(), 5);
+ assertEquals(flow.removeNulls().count(), 5);
}
@Test
Modified: tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java Tue Dec 28 02:12:27 2010
@@ -16,7 +16,6 @@ package org.apache.tapestry5.func;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.Test;
Modified: tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TakeDropTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TakeDropTests.java?rev=1053242&r1=1053241&r2=1053242&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TakeDropTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TakeDropTests.java Tue Dec 28 02:12:27 2010
@@ -45,7 +45,8 @@ public class TakeDropTests extends BaseF
@Test
public void take_and_drop()
{
- // This can go much, much larger but starts taking a while. Don't hold a reference to the start
+ // This can go much, much larger but starts taking a while. Don't hold a reference to the
+ // start
// of the series or it can run out of memory.
int length = 100000;
Added: tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TupleTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TupleTests.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TupleTests.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/TupleTests.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,58 @@
+package org.apache.tapestry5.func;
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.
+
+import org.apache.tapestry5.func.Tuple;
+import org.testng.annotations.Test;
+
+public class TupleTests extends BaseFuncTest
+{
+ private Tuple<String, Integer> t = Tuple.create("tapestry", 5);
+
+ @Test
+ public void tuple_to_string()
+ {
+ assertEquals(t.toString(), "(tapestry, 5)");
+ }
+
+ @Test
+ public void not_equal_null()
+ {
+ assertFalse(t.equals(null));
+ }
+
+ @Test
+ public void not_equal_anything_else()
+ {
+ assertFalse(t.equals("a string"));
+ }
+
+ @Test
+ public void values_must_be_equal()
+ {
+ assertFalse(t.equals(Tuple.create("tapestry", 4)));
+ }
+
+ @Test
+ void identity_is_equal()
+ {
+ assertTrue(t.equals(t));
+ }
+
+ @Test
+ public void equivalent_tuples_are_equal()
+ {
+ assertTrue(t.equals(Tuple.create("tapestry", 5)));
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/ZippedFlowTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/ZippedFlowTests.java?rev=1053242&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/ZippedFlowTests.java (added)
+++ tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/ZippedFlowTests.java Tue Dec 28 02:12:27 2010
@@ -0,0 +1,180 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.apache.tapestry5.func;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.lang.StringUtils;
+import org.testng.annotations.Test;
+
+public class ZippedFlowTests extends BaseFuncTest
+{
+ Flow<Integer> numbers = F.flow(1, 2, 3);
+
+ Flow<String> names = F.flow("fred", "barney", "wilma", "betty");
+
+ ZippedFlow<Integer, String> zipped = numbers.zipWith(names);
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void zip_flows_together()
+ {
+ assertListsEquals(zipped.toList(), Tuple.create(1, "fred"), Tuple.create(2, "barney"), Tuple.create(3, "wilma"));
+ }
+
+ @Test
+ public void unzip_zipped_flow()
+ {
+ Tuple<Flow<Integer>, Flow<String>> unzipped = zipped.drop(1).unzip();
+ Flow<Integer> unzippedNumbers = unzipped.first;
+ Flow<String> unzippedNames = unzipped.second;
+
+ assertListsEquals(unzippedNumbers.toList(), 2, 3);
+ assertListsEquals(unzippedNames.toList(), "barney", "wilma");
+ }
+
+ @Test
+ public void first_tuple_from_zipped_flow()
+ {
+ assertEquals(zipped.drop(2).first(), Tuple.create(3, "wilma"));
+ }
+
+ @Test
+ public void is_zipped_flow_empty()
+ {
+ assertFalse(zipped.isEmpty());
+
+ assertTrue(zipped.filter(F.isNull()).isEmpty());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void removeNulls()
+ {
+ Tuple<Integer, String> pebbles = Tuple.create(9, "pebbles");
+ ZippedFlow<Integer, String> extendedFlow = zipped.concat(Arrays.asList(null, pebbles, null));
+ ZippedFlow<Integer, String> noNulls = extendedFlow.removeNulls();
+
+ assertEquals(extendedFlow.count(), 6);
+ assertEquals(noNulls.count(), 4);
+
+ assertEquals(noNulls.reverse().seconds().first(), "pebbles");
+ }
+
+ @Test
+ public void rest_of_zipped_flow()
+ {
+ assertEquals(zipped.rest().first().second, "barney");
+ }
+
+ @Test
+ public void count_of_zipped_flow()
+ {
+ assertEquals(zipped.count(), 3);
+ }
+
+ @Test
+ public void take_from_zipped_flow()
+ {
+ assertEquals(zipped.take(2).reverse().first().second, "barney");
+ }
+
+ @Test
+ public void zipped_worker()
+ {
+ final AtomicInteger count = new AtomicInteger();
+
+ zipped.each(new Worker<Tuple<Integer, String>>()
+ {
+ @Override
+ public void work(Tuple<Integer, String> value)
+ {
+ count.addAndGet(value.second.length());
+ }
+
+ });
+
+ assertEquals(count.get(), 15);
+ }
+
+ @Test
+ public void reduce_zipped_flow()
+ {
+ int totalLength = zipped.reduce(new Reducer<Integer, Tuple<Integer, String>>()
+ {
+ public Integer reduce(Integer accumulator, Tuple<Integer, String> value)
+ {
+ return accumulator + value.second.length();
+ }
+
+ }, 0);
+
+ assertEquals(totalLength, 15);
+ }
+
+ @Test
+ public void remove_from_zipped_flow()
+ {
+ assertEquals(zipped.remove(F.notNull()).count(), 0);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void concat_a_zipped_flow()
+ {
+ Tuple<Integer, String> bambam = Tuple.create(4, "bam-bam");
+
+ List<Tuple<Integer, String>> asList = Arrays.asList(bambam);
+
+ ZippedFlow<Integer, String> zipped2 = zipped.concat(asList);
+
+ assertEquals(zipped2.count(), 4);
+
+ assertEquals(zipped2.reverse().seconds().first(), "bam-bam");
+ }
+
+ @Test
+ public void firsts()
+ {
+ assertEquals(zipped.reverse().firsts().first(), (Integer) 3);
+ }
+
+ @Test
+ public void seconds()
+ {
+ assertEquals(zipped.seconds().first(), "fred");
+ }
+
+ @Test
+ public void mapTuples()
+ {
+ Tuple<String, String> firstTuple = zipped.mapTuples(new Mapper<Tuple<Integer, String>, Tuple<String, String>>()
+ {
+
+ @Override
+ public Tuple<String, String> map(Tuple<Integer, String> value)
+ {
+ return Tuple.create(StringUtils.reverse(value.second),
+ String.format("%d-%d", value.first, value.second.length()));
+ }
+
+ }).first();
+
+ assertEquals(firstTuple.first, "derf");
+ assertEquals(firstTuple.second, "1-4");
+ }
+}