This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 8fcefb4  Centralize stream code in our own 'stream' package as a 
parallel to the JRE's java.util.stream package.
8fcefb4 is described below

commit 8fcefb4f98856998d857e90f73bb88af043ef3b8
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Wed Jun 17 17:37:14 2020 -0400

    Centralize stream code in our own 'stream' package as
    a parallel to the JRE's java.util.stream package.
    
    - Re-implement use of failable function interfaces based on our function
    package.
    - Deprecate old class.
    - Remove trainling whitespace.
---
 .../java/org/apache/commons/lang3/Functions.java   |   2 +-
 .../java/org/apache/commons/lang3/Streams.java     |   8 +
 .../org/apache/commons/lang3/concurrent/Locks.java |   2 +-
 .../apache/commons/lang3/function/Failable.java    |   2 +-
 .../org/apache/commons/lang3/stream/Streams.java   | 492 +++++++++++++++++++++
 .../java/org/apache/commons/lang3/StreamsTest.java |  16 +-
 .../apache/commons/lang3/stream/StreamsTest.java   | 187 ++++++++
 7 files changed, 698 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/Functions.java 
b/src/main/java/org/apache/commons/lang3/Functions.java
index 7c9ec3d..4cf9d55 100644
--- a/src/main/java/org/apache/commons/lang3/Functions.java
+++ b/src/main/java/org/apache/commons/lang3/Functions.java
@@ -31,6 +31,7 @@ import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
+import org.apache.commons.lang3.Streams.FailableStream;
 import org.apache.commons.lang3.function.FailableBooleanSupplier;
 import org.apache.commons.lang3.function.FailableDoubleBinaryOperator;
 import org.apache.commons.lang3.function.FailableDoubleConsumer;
@@ -39,7 +40,6 @@ import org.apache.commons.lang3.function.FailableIntConsumer;
 import org.apache.commons.lang3.function.FailableIntSupplier;
 import org.apache.commons.lang3.function.FailableLongConsumer;
 import org.apache.commons.lang3.function.FailableLongSupplier;
-import org.apache.commons.lang3.stream.Streams.FailableStream;
 
 /**
  * This class provides utility functions, and classes for working with the 
{@code java.util.function} package, or more
diff --git a/src/main/java/org/apache/commons/lang3/Streams.java 
b/src/main/java/org/apache/commons/lang3/Streams.java
index 125aefe..c6afd0a 100644
--- a/src/main/java/org/apache/commons/lang3/Streams.java
+++ b/src/main/java/org/apache/commons/lang3/Streams.java
@@ -62,14 +62,18 @@ import org.apache.commons.lang3.Functions.FailablePredicate;
  * @see Stream
  * @see Functions
  * @since 3.10
+ * @deprecated Use {@link org.apache.commons.lang3.stream.Streams}.
  */
+@Deprecated
 public class Streams {
 
    /**
     * A reduced, and simplified version of a {@link Stream} with
     * failable method signatures.
     * @param <O> The streams element type.
+    * @deprecated Use {@link 
org.apache.commons.lang3.stream.Streams.FailableStream}.
     */
+    @Deprecated
     public static class FailableStream<O extends Object> {
 
         private Stream<O> stream;
@@ -435,6 +439,10 @@ public class Streams {
         return stream(stream.stream());
     }
 
+    /**
+     * @deprecated Use {@link 
org.apache.commons.lang3.stream.Streams.ArrayCollector}.
+     */
+    @Deprecated
     public static class ArrayCollector<O> implements Collector<O, List<O>, 
O[]> {
         private static final Set<Characteristics> characteristics = 
Collections.emptySet();
         private final Class<O> elementType;
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/Locks.java 
b/src/main/java/org/apache/commons/lang3/concurrent/Locks.java
index 99cea4d..e96f36b 100644
--- a/src/main/java/org/apache/commons/lang3/concurrent/Locks.java
+++ b/src/main/java/org/apache/commons/lang3/concurrent/Locks.java
@@ -24,7 +24,7 @@ import org.apache.commons.lang3.function.FailableConsumer;
 import org.apache.commons.lang3.function.FailableFunction;
 
 
-/** 
+/**
  * Utility class for working with {@link java.util.concurrent.locks.Lock 
locked objects}. Locked objects are an
  * alternative to synchronization.
  *
diff --git a/src/main/java/org/apache/commons/lang3/function/Failable.java 
b/src/main/java/org/apache/commons/lang3/function/Failable.java
index bcc3e29..9ecc283 100644
--- a/src/main/java/org/apache/commons/lang3/function/Failable.java
+++ b/src/main/java/org/apache/commons/lang3/function/Failable.java
@@ -32,7 +32,7 @@ import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
-import org.apache.commons.lang3.Streams.FailableStream;
+import org.apache.commons.lang3.stream.Streams.FailableStream;
 
 /**
  * This class provides utility functions, and classes for working with the 
{@code java.util.function} package, or more
diff --git a/src/main/java/org/apache/commons/lang3/stream/Streams.java 
b/src/main/java/org/apache/commons/lang3/stream/Streams.java
new file mode 100644
index 0000000..1954432
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/stream/Streams.java
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.stream;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.function.Failable;
+import org.apache.commons.lang3.function.FailableConsumer;
+import org.apache.commons.lang3.function.FailableFunction;
+import org.apache.commons.lang3.function.FailablePredicate;
+
+/**
+ * Provides utility functions, and classes for working with the
+ * {@code java.util.stream} package, or more generally, with Java 8 lambdas. 
More
+ * specifically, it attempts to address the fact that lambdas are supposed
+ * not to throw Exceptions, at least not checked Exceptions, AKA instances
+ * of {@link Exception}. This enforces the use of constructs like
+ * <pre>
+ *     Consumer&lt;java.lang.reflect.Method&gt; consumer = (m) -&gt; {
+ *         try {
+ *             m.invoke(o, args);
+ *         } catch (Throwable t) {
+ *             throw Failable.rethrow(t);
+ *         }
+ *    };
+ *    stream.forEach(consumer);
+ * </pre>
+ * Using a {@link FailableStream}, this can be rewritten as follows:
+ * <pre>
+ *     Streams.failable(stream).forEach((m) -&gt; m.invoke(o, args));
+ * </pre>
+ * Obviously, the second version is much more concise and the spirit of
+ * Lambda expressions is met better than in the first version.
+ *
+ * @see Stream
+ * @see Failable
+ * @since 3.10
+ */
+public class Streams {
+
+   public static class ArrayCollector<O> implements Collector<O, List<O>, O[]> 
{
+    private static final Set<Characteristics> characteristics = 
Collections.emptySet();
+    private final Class<O> elementType;
+
+    public ArrayCollector(final Class<O> elementType) {
+        this.elementType = elementType;
+    }
+
+    @Override
+    public BiConsumer<List<O>, O> accumulator() {
+        return List::add;
+    }
+
+    @Override
+    public Set<Characteristics> characteristics() {
+        return characteristics;
+    }
+
+    @Override
+    public BinaryOperator<List<O>> combiner() {
+        return (left, right) -> {
+            left.addAll(right);
+            return left;
+        };
+    }
+
+    @Override
+    public Function<List<O>, O[]> finisher() {
+        return list -> {
+            @SuppressWarnings("unchecked")
+            final O[] array = (O[]) Array.newInstance(elementType, 
list.size());
+            return list.toArray(array);
+        };
+    }
+
+    @Override
+    public Supplier<List<O>> supplier() {
+        return ArrayList::new;
+    }
+}
+
+    /**
+        * A reduced, and simplified version of a {@link Stream} with
+        * failable method signatures.
+        * @param <O> The streams element type.
+        */
+        public static class FailableStream<O extends Object> {
+
+            private Stream<O> stream;
+            private boolean terminated;
+
+            /**
+             * Constructs a new instance with the given {@code stream}.
+             * @param stream The stream.
+             */
+            public FailableStream(final Stream<O> stream) {
+                this.stream = stream;
+            }
+
+            /**
+             * Returns whether all elements of this stream match the provided 
predicate.
+             * May not evaluate the predicate on all elements if not necessary 
for
+             * determining the result.  If the stream is empty then {@code 
true} is
+             * returned and the predicate is not evaluated.
+             *
+             * <p>This is a short-circuiting terminal operation.
+             *
+             * \@apiNote
+             * This method evaluates the <em>universal quantification</em> of 
the
+             * predicate over the elements of the stream (for all x P(x)).  If 
the
+             * stream is empty, the quantification is said to be <em>vacuously
+             * satisfied</em> and is always {@code true} (regardless of P(x)).
+             *
+             * @param predicate A non-interfering, stateless predicate to 
apply to
+             * elements of this stream
+             * @return {@code true} If either all elements of the stream match 
the
+             * provided predicate or the stream is empty, otherwise {@code 
false}.
+             */
+            public boolean allMatch(final FailablePredicate<O, ?> predicate) {
+                assertNotTerminated();
+                return stream().allMatch(Failable.asPredicate(predicate));
+            }
+
+            /**
+             * Returns whether any elements of this stream match the provided
+             * predicate.  May not evaluate the predicate on all elements if 
not
+             * necessary for determining the result.  If the stream is empty 
then
+             * {@code false} is returned and the predicate is not evaluated.
+             *
+             * <p>This is a short-circuiting terminal operation.
+             *
+             * \@apiNote
+             * This method evaluates the <em>existential quantification</em> 
of the
+             * predicate over the elements of the stream (for some x P(x)).
+             *
+             * @param predicate A non-interfering, stateless predicate to 
apply to
+             * elements of this stream
+             * @return {@code true} if any elements of the stream match the 
provided
+             * predicate, otherwise {@code false}
+             */
+            public boolean anyMatch(final FailablePredicate<O, ?> predicate) {
+                assertNotTerminated();
+                return stream().anyMatch(Failable.asPredicate(predicate));
+            }
+
+            protected void assertNotTerminated() {
+                if (terminated) {
+                    throw new IllegalStateException("This stream is already 
terminated.");
+                }
+            }
+
+            /**
+             * Performs a mutable reduction operation on the elements of this 
stream using a
+             * {@code Collector}.  A {@code Collector}
+             * encapsulates the functions used as arguments to
+             * {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing 
for reuse of
+             * collection strategies and composition of collect operations 
such as
+             * multiple-level grouping or partitioning.
+             *
+             * <p>If the underlying stream is parallel, and the {@code 
Collector}
+             * is concurrent, and either the stream is unordered or the 
collector is
+             * unordered, then a concurrent reduction will be performed
+             * (see {@link Collector} for details on concurrent reduction.)
+             *
+             * <p>This is a terminal operation.
+             *
+             * <p>When executed in parallel, multiple intermediate results may 
be
+             * instantiated, populated, and merged so as to maintain isolation 
of
+             * mutable data structures.  Therefore, even when executed in 
parallel
+             * with non-thread-safe data structures (such as {@code 
ArrayList}), no
+             * additional synchronization is needed for a parallel reduction.
+             *
+             * \@apiNote
+             * The following will accumulate strings into an ArrayList:
+             * <pre>{@code
+             *     List<String> asList = 
stringStream.collect(Collectors.toList());
+             * }</pre>
+             *
+             * <p>The following will classify {@code Person} objects by city:
+             * <pre>{@code
+             *     Map<String, List<Person>> peopleByCity
+             *         = 
personStream.collect(Collectors.groupingBy(Person::getCity));
+             * }</pre>
+             *
+             * <p>The following will classify {@code Person} objects by state 
and city,
+             * cascading two {@code Collector}s together:
+             * <pre>{@code
+             *     Map<String, Map<String, List<Person>>> peopleByStateAndCity
+             *         = 
personStream.collect(Collectors.groupingBy(Person::getState,
+             *                                                      
Collectors.groupingBy(Person::getCity)));
+             * }</pre>
+             *
+             * @param <R> the type of the result
+             * @param <A> the intermediate accumulation type of the {@code 
Collector}
+             * @param collector the {@code Collector} describing the reduction
+             * @return the result of the reduction
+             * @see #collect(Supplier, BiConsumer, BiConsumer)
+             * @see Collectors
+             */
+            public <A, R> R collect(final Collector<? super O, A, R> 
collector) {
+                makeTerminated();
+                return stream().collect(collector);
+            }
+
+            /**
+             * Performs a mutable reduction operation on the elements of this 
FailableStream.
+             * A mutable reduction is one in which the reduced value is a 
mutable result
+             * container, such as an {@code ArrayList}, and elements are 
incorporated by updating
+             * the state of the result rather than by replacing the result. 
This produces a result equivalent to:
+             * <pre>{@code
+             *     R result = supplier.get();
+             *     for (T element : this stream)
+             *         accumulator.accept(result, element);
+             *     return result;
+             * }</pre>
+             *
+             * <p>Like {@link #reduce(Object, BinaryOperator)}, {@code 
collect} operations
+             * can be parallelized without requiring additional 
synchronization.
+             *
+             * <p>This is a terminal operation.
+             *
+             * \@apiNote There are many existing classes in the JDK whose 
signatures are
+             * well-suited for use with method references as arguments to 
{@code collect()}.
+             * For example, the following will accumulate strings into an 
{@code ArrayList}:
+             * <pre>{@code
+             *     List<String> asList = stringStream.collect(ArrayList::new, 
ArrayList::add,
+             *                                                
ArrayList::addAll);
+             * }</pre>
+             *
+             * <p>The following will take a stream of strings and concatenates 
them into a
+             * single string:
+             * <pre>{@code
+             *     String concat = stringStream.collect(StringBuilder::new, 
StringBuilder::append,
+             *                                          StringBuilder::append)
+             *                                 .toString();
+             * }</pre>
+             *
+             * @param <R> type of the result
+             * @param <A> Type of the accumulator.
+             * @param pupplier a function that creates a new result container. 
For a
+             *                 parallel execution, this function may be called
+             *                 multiple times and must return a fresh value 
each time.
+             * @param accumulator An associative, non-interfering, stateless 
function for
+             *   incorporating an additional element into a result
+             * @param combiner An associative, non-interfering, stateless
+             *   function for combining two values, which must be compatible 
with the
+             *   accumulator function
+             * @return The result of the reduction
+             */
+            public <A, R> R collect(final Supplier<R> pupplier, final 
BiConsumer<R, ? super O> accumulator, final BiConsumer<R, R> combiner) {
+                makeTerminated();
+                return stream().collect(pupplier, accumulator, combiner);
+            }
+
+            /**
+             * Returns a FailableStream consisting of the elements of this 
stream that match
+             * the given FailablePredicate.
+             *
+             * <p>This is an intermediate operation.
+             *
+             * @param predicate a non-interfering, stateless predicate to 
apply to each
+             * element to determine if it should be included.
+             * @return the new stream
+             */
+            public FailableStream<O> filter(final FailablePredicate<O, ?> 
predicate){
+                assertNotTerminated();
+                stream = stream.filter(Failable.asPredicate(predicate));
+                return this;
+            }
+
+            /**
+             * Performs an action for each element of this stream.
+             *
+             * <p>This is a terminal operation.
+             *
+             * <p>The behavior of this operation is explicitly 
nondeterministic.
+             * For parallel stream pipelines, this operation does <em>not</em>
+             * guarantee to respect the encounter order of the stream, as 
doing so
+             * would sacrifice the benefit of parallelism.  For any given 
element, the
+             * action may be performed at whatever time and in whatever thread 
the
+             * library chooses.  If the action accesses shared state, it is
+             * responsible for providing the required synchronization.
+             *
+             * @param action a non-interfering action to perform on the 
elements
+             */
+            public void forEach(final FailableConsumer<O, ?> action) {
+                makeTerminated();
+                stream().forEach(Failable.asConsumer(action));
+            }
+
+            protected void makeTerminated() {
+                assertNotTerminated();
+                terminated = true;
+            }
+
+            /**
+             * Returns a stream consisting of the results of applying the given
+             * function to the elements of this stream.
+             *
+             * <p>This is an intermediate operation.
+             *
+             * @param <R> The element type of the new stream
+             * @param mapper A non-interfering, stateless function to apply to 
each element
+             * @return the new stream
+             */
+            public <R> FailableStream<R> map(final FailableFunction<O, R, ?> 
mapper) {
+                assertNotTerminated();
+                return new 
FailableStream<>(stream.map(Failable.asFunction(mapper)));
+            }
+
+            /**
+             * Performs a reduction on the elements of this stream, using the 
provided
+             * identity value and an associative accumulation function, and 
returns
+             * the reduced value.  This is equivalent to:
+             * <pre>{@code
+             *     T result = identity;
+             *     for (T element : this stream)
+             *         result = accumulator.apply(result, element)
+             *     return result;
+             * }</pre>
+             *
+             * but is not constrained to execute sequentially.
+             *
+             * <p>The {@code identity} value must be an identity for the 
accumulator
+             * function. This means that for all {@code t},
+             * {@code accumulator.apply(identity, t)} is equal to {@code t}.
+             * The {@code accumulator} function must be an associative 
function.
+             *
+             * <p>This is a terminal operation.
+             *
+             * \@apiNote Sum, min, max, average, and string concatenation are 
all special
+             * cases of reduction. Summing a stream of numbers can be 
expressed as:
+             *
+             * <pre>{@code
+             *     Integer sum = integers.reduce(0, (a, b) -> a+b);
+             * }</pre>
+             *
+             * or:
+             *
+             * <pre>{@code
+             *     Integer sum = integers.reduce(0, Integer::sum);
+             * }</pre>
+             *
+             * <p>While this may seem a more roundabout way to perform an 
aggregation
+             * compared to simply mutating a running total in a loop, reduction
+             * operations parallelize more gracefully, without needing 
additional
+             * synchronization and with greatly reduced risk of data races.
+             *
+             * @param identity the identity value for the accumulating function
+             * @param accumulator an associative, non-interfering, stateless
+             *                    function for combining two values
+             * @return the result of the reduction
+             */
+            public O reduce(final O identity, final BinaryOperator<O> 
accumulator) {
+                makeTerminated();
+                return stream().reduce(identity, accumulator);
+            }
+
+            /**
+             * Converts the FailableStream into an equivalent stream.
+             * @return A stream, which will return the same elements, which 
this FailableStream would return.
+             */
+            public Stream<O> stream() {
+                return stream;
+            }
+        }
+
+    /**
+     * Converts the given {@link Collection} into a {@link FailableStream}.
+     * This is basically a simplified, reduced version of the {@link Stream}
+     * class, with the same underlying element stream, except that failable
+     * objects, like {@link FailablePredicate}, {@link FailableFunction}, or
+     * {@link FailableConsumer} may be applied, instead of
+     * {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
+     * to rewrite a code snippet like this:
+     * <pre>
+     *     final List&lt;O&gt; list;
+     *     final Method m;
+     *     final Function&lt;O,String&gt; mapper = (o) -&gt; {
+     *         try {
+     *             return (String) m.invoke(o);
+     *         } catch (Throwable t) {
+     *             throw Failable.rethrow(t);
+     *         }
+     *     };
+     *     final List&lt;String&gt; strList = list.stream()
+     *         .map(mapper).collect(Collectors.toList());
+     *  </pre>
+     *  as follows:
+     *  <pre>
+     *     final List&lt;O&gt; list;
+     *     final Method m;
+     *     final List&lt;String&gt; strList = Failable.stream(list.stream())
+     *         .map((o) -&gt; (String) 
m.invoke(o)).collect(Collectors.toList());
+     *  </pre>
+     *  While the second version may not be <em>quite</em> as
+     *  efficient (because it depends on the creation of additional,
+     *  intermediate objects, of type FailableStream), it is much more
+     *  concise, and readable, and meets the spirit of Lambdas better
+     *  than the first version.
+     * @param <O> The streams element type.
+     * @param stream The stream, which is being converted.
+     * @return The {@link FailableStream}, which has been created by
+     *   converting the stream.
+     */
+    public static <O> FailableStream<O> stream(final Collection<O> stream) {
+        return stream(stream.stream());
+    }
+
+    /**
+     * Converts the given {@link Stream stream} into a {@link FailableStream}.
+     * This is basically a simplified, reduced version of the {@link Stream}
+     * class, with the same underlying element stream, except that failable
+     * objects, like {@link FailablePredicate}, {@link FailableFunction}, or
+     * {@link FailableConsumer} may be applied, instead of
+     * {@link Predicate}, {@link Function}, or {@link Consumer}. The idea is
+     * to rewrite a code snippet like this:
+     * <pre>
+     *     final List&lt;O&gt; list;
+     *     final Method m;
+     *     final Function&lt;O,String&gt; mapper = (o) -&gt; {
+     *         try {
+     *             return (String) m.invoke(o);
+     *         } catch (Throwable t) {
+     *             throw Failable.rethrow(t);
+     *         }
+     *     };
+     *     final List&lt;String&gt; strList = list.stream()
+     *         .map(mapper).collect(Collectors.toList());
+     *  </pre>
+     *  as follows:
+     *  <pre>
+     *     final List&lt;O&gt; list;
+     *     final Method m;
+     *     final List&lt;String&gt; strList = Failable.stream(list.stream())
+     *         .map((o) -&gt; (String) 
m.invoke(o)).collect(Collectors.toList());
+     *  </pre>
+     *  While the second version may not be <em>quite</em> as
+     *  efficient (because it depends on the creation of additional,
+     *  intermediate objects, of type FailableStream), it is much more
+     *  concise, and readable, and meets the spirit of Lambdas better
+     *  than the first version.
+     * @param <O> The streams element type.
+     * @param stream The stream, which is being converted.
+     * @return The {@link FailableStream}, which has been created by
+     *   converting the stream.
+     */
+    public static <O> FailableStream<O> stream(final Stream<O> stream) {
+        return new FailableStream<>(stream);
+    }
+
+    /**
+     * Returns a {@code Collector} that accumulates the input elements into a
+     * new array.
+     *
+     * @param pElementType Type of an element in the array.
+     * @param <O> the type of the input elements
+     * @return a {@code Collector} which collects all the input elements into 
an
+     * array, in encounter order
+     */
+    public static <O extends Object> Collector<O, ?, O[]> toArray(final 
Class<O> pElementType) {
+        return new ArrayCollector<>(pElementType);
+    }
+}
diff --git a/src/test/java/org/apache/commons/lang3/StreamsTest.java 
b/src/test/java/org/apache/commons/lang3/StreamsTest.java
index a0b79f6..df13160 100644
--- a/src/test/java/org/apache/commons/lang3/StreamsTest.java
+++ b/src/test/java/org/apache/commons/lang3/StreamsTest.java
@@ -41,10 +41,10 @@ import org.junit.jupiter.api.TestFactory;
 import org.junit.jupiter.api.function.Executable;
 import org.xml.sax.SAXException;
 
-class StreamsTest {
+public class StreamsTest {
 
     @Test
-    void testSimpleStreamMap() {
+    public void testSimpleStreamMap() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
         final List<Integer> output = 
Functions.stream(input).map(Integer::valueOf).collect(Collectors.toList());
         assertEquals(6, output.size());
@@ -54,7 +54,7 @@ class StreamsTest {
     }
 
     @Test
-    void testSimpleStreamMapFailing() {
+    public void testSimpleStreamMapFailing() {
         final List<String> input = Arrays.asList("1", "2", "3", "4 ", "5", 
"6");
         final Executable testMethod = () -> 
Functions.stream(input).map(Integer::valueOf).collect(Collectors.toList());
         final NumberFormatException thrown = 
assertThrows(NumberFormatException.class, testMethod);
@@ -62,7 +62,7 @@ class StreamsTest {
     }
 
     @Test
-    void testSimpleStreamForEach() {
+    public void testSimpleStreamForEach() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
         final List<Integer> output = new ArrayList<>();
         Functions.stream(input).forEach(s -> output.add(Integer.valueOf(s)));
@@ -73,7 +73,7 @@ class StreamsTest {
     }
 
     @Test
-    void testToArray() {
+    public void testToArray() {
         final String[] array = Arrays.asList("2", "3", 
"1").stream().collect(Streams.toArray(String.class));
         assertNotNull(array);
         assertEquals(3, array.length);
@@ -92,7 +92,7 @@ class StreamsTest {
     }
 
     @TestFactory
-    Stream<DynamicTest> simpleStreamForEachFailing() {
+    public Stream<DynamicTest> simpleStreamForEachFailing() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
 
         return Stream.of(
@@ -127,7 +127,7 @@ class StreamsTest {
     }
 
     @Test
-    void testSimpleStreamFilter() {
+    public void testSimpleStreamFilter() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
         final List<Integer> output = Functions.stream(input)
                 .map(Integer::valueOf)
@@ -155,7 +155,7 @@ class StreamsTest {
     }
 
     @TestFactory
-    Stream<DynamicTest> simpleStreamFilterFailing() {
+    public Stream<DynamicTest> simpleStreamFilterFailing() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
         final List<Integer> output = Functions.stream(input)
                 .map(Integer::valueOf)
diff --git a/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java 
b/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java
new file mode 100644
index 0000000..218fa33
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.stream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.DynamicTest.dynamicTest;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.function.Failable;
+import org.apache.commons.lang3.function.FailableConsumer;
+import org.apache.commons.lang3.function.FailablePredicate;
+import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestFactory;
+import org.junit.jupiter.api.function.Executable;
+import org.xml.sax.SAXException;
+
+public class StreamsTest {
+
+    protected <T extends Throwable> FailableConsumer<String, T> 
asIntConsumer(final T pThrowable) {
+        return s -> {
+            final Integer i = Integer.valueOf(s);
+            if (i.intValue() == 4) {
+                throw pThrowable;
+            }
+        };
+    }
+
+    protected <T extends Throwable> FailablePredicate<Integer, T> 
asIntPredicate(final T pThrowable) {
+        return i -> {
+            if (i.intValue() == 5) {
+                if (pThrowable != null) {
+                    throw pThrowable;
+                }
+            }
+            return i % 2 == 0;
+        };
+    }
+
+    private void assertEvenNumbers(final List<Integer> output) {
+        assertEquals(3, output.size());
+        for (int i = 0; i < 3; i++) {
+            assertEquals((i + 1) * 2, output.get(i).intValue());
+        }
+    }
+
+    @TestFactory
+    public Stream<DynamicTest> simpleStreamFilterFailing() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
+        final List<Integer> output = 
Failable.stream(input).map(Integer::valueOf).filter(asIntPredicate(null))
+            .collect(Collectors.toList());
+        assertEvenNumbers(output);
+
+        return Stream.of(
+
+            dynamicTest("IllegalArgumentException", () -> {
+                final IllegalArgumentException iae = new 
IllegalArgumentException("Invalid argument: " + 5);
+                final Executable testMethod = () -> 
Failable.stream(input).map(Integer::valueOf)
+                    .filter(asIntPredicate(iae)).collect(Collectors.toList());
+                final IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, testMethod);
+                assertThat(thrown.getMessage(), is(equalTo("Invalid argument: 
" + 5)));
+            }),
+
+            dynamicTest("OutOfMemoryError", () -> {
+                final OutOfMemoryError oome = new OutOfMemoryError();
+                final Executable testMethod = () -> 
Failable.stream(input).map(Integer::valueOf)
+                    .filter(asIntPredicate(oome)).collect(Collectors.toList());
+                final OutOfMemoryError thrown = 
assertThrows(OutOfMemoryError.class, testMethod);
+                assertThat(thrown.getMessage(), is(nullValue()));
+            }),
+
+            dynamicTest("SAXException", () -> {
+                final SAXException se = new SAXException();
+                final Executable testMethod = () -> 
Failable.stream(input).map(Integer::valueOf)
+                    .filter(asIntPredicate(se)).collect(Collectors.toList());
+                final UndeclaredThrowableException thrown = 
assertThrows(UndeclaredThrowableException.class,
+                    testMethod);
+                assertAll(() -> assertThat(thrown.getMessage(), 
is(nullValue())),
+                    () -> assertThat(thrown.getCause(), is(equalTo(se))));
+            }));
+    }
+
+    @TestFactory
+    public Stream<DynamicTest> simpleStreamForEachFailing() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
+
+        return Stream.of(
+
+            dynamicTest("IllegalArgumentException", () -> {
+                final IllegalArgumentException ise = new 
IllegalArgumentException();
+                final Executable testMethod = () -> 
Failable.stream(input).forEach(asIntConsumer(ise));
+                final IllegalArgumentException thrown = 
assertThrows(IllegalArgumentException.class, testMethod);
+                assertThat(thrown.getMessage(), is(nullValue()));
+            }),
+
+            dynamicTest("OutOfMemoryError", () -> {
+                final OutOfMemoryError oome = new OutOfMemoryError();
+                final Executable oomeTestMethod = () -> 
Failable.stream(input).forEach(asIntConsumer(oome));
+                final OutOfMemoryError oomeThrown = 
assertThrows(OutOfMemoryError.class, oomeTestMethod);
+                assertThat(oomeThrown.getMessage(), is(nullValue()));
+            }),
+
+            dynamicTest("SAXException", () -> {
+                final SAXException se = new SAXException();
+                final Executable seTestMethod = () -> 
Failable.stream(input).forEach(asIntConsumer(se));
+                final UndeclaredThrowableException seThrown = 
assertThrows(UndeclaredThrowableException.class,
+                    seTestMethod);
+                assertAll(() -> assertThat(seThrown.getMessage(), 
is(nullValue())),
+                    () -> assertThat(seThrown.getCause(), is(equalTo(se))));
+            }));
+    }
+
+    @Test
+    public void testSimpleStreamFilter() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
+        final List<Integer> output = 
Failable.stream(input).map(Integer::valueOf).filter(i -> (i.intValue() % 2 == 
0))
+            .collect(Collectors.toList());
+        assertEvenNumbers(output);
+    }
+
+    @Test
+    public void testSimpleStreamForEach() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
+        final List<Integer> output = new ArrayList<>();
+        Failable.stream(input).forEach(s -> output.add(Integer.valueOf(s)));
+        assertEquals(6, output.size());
+        for (int i = 0; i < 6; i++) {
+            assertEquals(i + 1, output.get(i).intValue());
+        }
+    }
+
+    @Test
+    public void testSimpleStreamMap() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");
+        final List<Integer> output = 
Failable.stream(input).map(Integer::valueOf).collect(Collectors.toList());
+        assertEquals(6, output.size());
+        for (int i = 0; i < 6; i++) {
+            assertEquals(i + 1, output.get(i).intValue());
+        }
+    }
+
+    @Test
+    public void testSimpleStreamMapFailing() {
+        final List<String> input = Arrays.asList("1", "2", "3", "4 ", "5", 
"6");
+        final Executable testMethod = () -> 
Failable.stream(input).map(Integer::valueOf).collect(Collectors.toList());
+        final NumberFormatException thrown = 
assertThrows(NumberFormatException.class, testMethod);
+        assertEquals("For input string: \"4 \"", thrown.getMessage());
+    }
+
+    @Test
+    public void testToArray() {
+        final String[] array = Arrays.asList("2", "3", 
"1").stream().collect(Streams.toArray(String.class));
+        assertNotNull(array);
+        assertEquals(3, array.length);
+        assertEquals("2", array[0]);
+        assertEquals("3", array[1]);
+        assertEquals("1", array[2]);
+    }
+
+}

Reply via email to