You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/08/12 17:58:29 UTC

[commons-lang] branch master updated (969a9d9f1 -> 6a95f03a6)

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

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


    from 969a9d9f1 Bump actions/cache from 3.0.6 to 3.0.7
     new 62910e4f3 Add LangCollectors
     new 6a95f03a6 Sort members

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/changes/changes.xml                            |   1 +
 .../java/org/apache/commons/lang3/StringUtils.java |  21 +--
 .../commons/lang3/stream/LangCollectors.java       | 167 +++++++++++++++++++++
 .../commons/lang3/stream/LangCollectorsTest.java   | 141 +++++++++++++++++
 .../apache/commons/lang3/stream/StreamsTest.java   |  22 +--
 5 files changed, 325 insertions(+), 27 deletions(-)
 create mode 100644 src/main/java/org/apache/commons/lang3/stream/LangCollectors.java
 create mode 100644 src/test/java/org/apache/commons/lang3/stream/LangCollectorsTest.java


[commons-lang] 01/02: Add LangCollectors

Posted by gg...@apache.org.
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

commit 62910e4f329923c77bc8bd2156d56544d1119efd
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Aug 12 11:33:10 2022 -0400

    Add LangCollectors
---
 src/changes/changes.xml                            |   1 +
 .../java/org/apache/commons/lang3/StringUtils.java |  21 +--
 .../commons/lang3/stream/LangCollectors.java       | 167 +++++++++++++++++++++
 .../commons/lang3/stream/LangCollectorsTest.java   | 141 +++++++++++++++++
 4 files changed, 314 insertions(+), 16 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index aa1afbda2..f1f71c10d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -160,6 +160,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action issue="LANG-1662" type="add" dev="ggregory" due-to="Daniel Augusto Veronezi Salvador, Gary Gregory, Bruno P. Kinoshita">Let ReflectionToStringBuilder only reflect given field names #849.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add Streams.of(Enumeration&lt;E&gt;).</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add Streams.of(Iterable&lt;E&gt;).</action>
+    <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add LangCollectors.</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Dependabot, XenoAmess, Gary Gregory">Bump actions/cache from 2.1.4 to 3.0.7 #742, #752, #764, #833, #867.</action>
     <action                   type="update" dev="ggregory" due-to="Dependabot">Bump actions/checkout from 2 to 3 #819, #825, #859.</action>
diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java
index 8b49f66ab..2960c83f7 100644
--- a/src/main/java/org/apache/commons/lang3/StringUtils.java
+++ b/src/main/java/org/apache/commons/lang3/StringUtils.java
@@ -27,12 +27,13 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
-import java.util.StringJoiner;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 
 import org.apache.commons.lang3.function.Suppliers;
 import org.apache.commons.lang3.function.ToBooleanBiFunction;
+import org.apache.commons.lang3.stream.LangCollectors;
 
 /**
  * <p>Operations on {@link java.lang.String} that are
@@ -4705,10 +4706,7 @@ public class StringUtils {
      * @return the joined String, {@code null} if null array input
      */
     public static String join(final Object[] array, final String delimiter) {
-        if (array == null) {
-            return null;
-        }
-        return join(array, delimiter, 0, array.length);
+        return array != null ? join(array, toStringOrEmpty(delimiter), 0, array.length) : null;
     }
 
     /**
@@ -4747,17 +4745,8 @@ public class StringUtils {
      * {@code endIndex > array.length()}
      */
     public static String join(final Object[] array, final String delimiter, final int startIndex, final int endIndex) {
-        if (array == null) {
-            return null;
-        }
-        if (endIndex - startIndex <= 0) {
-            return EMPTY;
-        }
-        final StringJoiner joiner = new StringJoiner(toStringOrEmpty(delimiter));
-        for (int i = startIndex; i < endIndex; i++) {
-            joiner.add(toStringOrEmpty(array[i]));
-        }
-        return joiner.toString();
+        return array != null ? Stream.of(array).skip(startIndex).limit(Math.max(0, endIndex - startIndex))
+            .collect(LangCollectors.joining(delimiter, EMPTY, EMPTY, StringUtils::toStringOrEmpty)) : null;
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/lang3/stream/LangCollectors.java b/src/main/java/org/apache/commons/lang3/stream/LangCollectors.java
new file mode 100644
index 000000000..abd3b10f8
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/stream/LangCollectors.java
@@ -0,0 +1,167 @@
+/*
+ * 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.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Implementations of {@link Collector} that implement various useful reduction operations.
+ * <p>
+ * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}.
+ * </p>
+ *
+ * @since 3.13.0
+ */
+public final class LangCollectors {
+
+    /**
+     * Simple implementation class for {@code Collector}.
+     *
+     * @param <T> the type of elements to be collected
+     * @param <R> the type of the result
+     */
+    private static class SimpleCollector<T, A, R> implements Collector<T, A, R> {
+
+        private final BiConsumer<A, T> accumulator;
+        private final Set<Characteristics> characteristics;
+        private final BinaryOperator<A> combiner;
+        private final Function<A, R> finisher;
+        private final Supplier<A> supplier;
+
+        private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher,
+            final Set<Characteristics> characteristics) {
+            this.supplier = supplier;
+            this.accumulator = accumulator;
+            this.combiner = combiner;
+            this.finisher = finisher;
+            this.characteristics = characteristics;
+        }
+
+        @Override
+        public BiConsumer<A, T> accumulator() {
+            return accumulator;
+        }
+
+        @Override
+        public Set<Characteristics> characteristics() {
+            return characteristics;
+        }
+
+        @Override
+        public BinaryOperator<A> combiner() {
+            return combiner;
+        }
+
+        @Override
+        public Function<A, R> finisher() {
+            return finisher;
+        }
+
+        @Override
+        public Supplier<A> supplier() {
+            return supplier;
+        }
+    }
+
+    private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
+
+    /**
+     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter
+     * order.
+     * <p>
+     * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
+     * </p>
+     *
+     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter
+     *         order.
+     */
+    public static Collector<Object, ?, String> joining() {
+        return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
+    }
+
+    /**
+     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter
+     * order.
+     * <p>
+     * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just
+     * {@code CharSequence}.
+     * </p>
+     *
+     * @param delimiter the delimiter to be used between each element.
+     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter
+     *         order.
+     */
+    public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
+        return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
+    }
+
+    /**
+     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
+     * specified prefix and suffix, in encounter order.
+     * <p>
+     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
+     * element class, not just {@code CharSequence}.
+     * </p>
+     *
+     * @param delimiter the delimiter to be used between each element
+     * @param prefix the sequence of characters to be used at the beginning of the joined result
+     * @param suffix the sequence of characters to be used at the end of the joined result
+     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
+     *         encounter order
+     */
+    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
+        return joining(delimiter, prefix, suffix, Objects::toString);
+    }
+
+    /**
+     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
+     * specified prefix and suffix, in encounter order.
+     * <p>
+     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
+     * element class, not just {@code CharSequence}.
+     * </p>
+     *
+     * @param delimiter the delimiter to be used between each element
+     * @param prefix the sequence of characters to be used at the beginning of the joined result
+     * @param suffix the sequence of characters to be used at the end of the joined result
+     * @param toString A function that takes an Object and returns a non-null String.
+     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
+     *         encounter order
+     */
+    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
+        final Function<Object, String> toString) {
+        return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
+            StringJoiner::toString, CH_NOID);
+    }
+
+    private LangCollectors() {
+        // No instance
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/lang3/stream/LangCollectorsTest.java b/src/test/java/org/apache/commons/lang3/stream/LangCollectorsTest.java
new file mode 100644
index 000000000..e8f408498
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/stream/LangCollectorsTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link LangCollectors}
+ */
+public class LangCollectorsTest {
+
+    private static class Fixture {
+        int value;
+
+        private Fixture(final int value) {
+            this.value = value;
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toString(value);
+        }
+    }
+
+    private static final Long _1L = Long.valueOf(1);
+    private static final Long _2L = Long.valueOf(2);
+    private static final Long _3L = Long.valueOf(3);
+
+    private static final Function<Object, String> TO_STRING = Objects::toString;
+
+    private static final Collector<Object, ?, String> JOINING_0 = LangCollectors.joining();
+    private static final Collector<Object, ?, String> JOINING_1 = LangCollectors.joining("-");
+    private static final Collector<Object, ?, String> JOINING_3 = LangCollectors.joining("-", "<", ">");
+    private static final Collector<Object, ?, String> JOINING_4 = LangCollectors.joining("-", "<", ">", TO_STRING);
+    private static final Collector<Object, ?, String> JOINING_4_NUL = LangCollectors.joining("-", "<", ">", o -> Objects.toString(o, "NUL"));
+
+    @Test
+    public void testJoiningNonStrings0Arg() {
+        assertEquals("", Stream.of().collect(JOINING_0));
+        assertEquals("1", Stream.of(_1L).collect(JOINING_0));
+        assertEquals("12", Stream.of(_1L, _2L).collect(JOINING_0));
+        assertEquals("123", Stream.of(_1L, _2L, _3L).collect(JOINING_0));
+        assertEquals("1null3", Stream.of(_1L, null, _3L).collect(JOINING_0));
+        assertEquals("12", Stream.of(new AtomicLong(1), new AtomicLong(2)).collect(JOINING_0));
+        assertEquals("12", Stream.of(new Fixture(1), new Fixture(2)).collect(JOINING_0));
+    }
+
+    @Test
+    public void testJoiningNonStrings1Arg() {
+        assertEquals("", Stream.of().collect(JOINING_1));
+        assertEquals("1", Stream.of(_1L).collect(JOINING_1));
+        assertEquals("1-2", Stream.of(_1L, _2L).collect(JOINING_1));
+        assertEquals("1-2-3", Stream.of(_1L, _2L, _3L).collect(JOINING_1));
+        assertEquals("1-null-3", Stream.of(_1L, null, _3L).collect(JOINING_1));
+        assertEquals("1-2", Stream.of(new AtomicLong(1), new AtomicLong(2)).collect(JOINING_1));
+        assertEquals("1-2", Stream.of(new Fixture(1), new Fixture(2)).collect(JOINING_1));
+    }
+
+    @Test
+    public void testJoiningNonStrings3Args() {
+        assertEquals("<>", Stream.of().collect(JOINING_3));
+        assertEquals("<1>", Stream.of(_1L).collect(JOINING_3));
+        assertEquals("<1-2>", Stream.of(_1L, _2L).collect(JOINING_3));
+        assertEquals("<1-2-3>", Stream.of(_1L, _2L, _3L).collect(JOINING_3));
+        assertEquals("<1-null-3>", Stream.of(_1L, null, _3L).collect(JOINING_3));
+        assertEquals("<1-2>", Stream.of(new AtomicLong(1), new AtomicLong(2)).collect(JOINING_3));
+        assertEquals("<1-2>", Stream.of(new Fixture(1), new Fixture(2)).collect(JOINING_3));
+    }
+
+    @Test
+    public void testJoiningNonStrings4Args() {
+        assertEquals("<>", Stream.of().collect(JOINING_4));
+        assertEquals("<1>", Stream.of(_1L).collect(JOINING_4));
+        assertEquals("<1-2>", Stream.of(_1L, _2L).collect(JOINING_4));
+        assertEquals("<1-2-3>", Stream.of(_1L, _2L, _3L).collect(JOINING_4));
+        assertEquals("<1-null-3>", Stream.of(_1L, null, _3L).collect(JOINING_4));
+        assertEquals("<1-NUL-3>", Stream.of(_1L, null, _3L).collect(JOINING_4_NUL));
+        assertEquals("<1-2>", Stream.of(new AtomicLong(1), new AtomicLong(2)).collect(JOINING_4));
+        assertEquals("<1-2>", Stream.of(new Fixture(1), new Fixture(2)).collect(JOINING_4));
+    }
+
+    @Test
+    public void testJoiningStrings0Arg() {
+        assertEquals("", Stream.of().collect(JOINING_0));
+        assertEquals("1", Stream.of("1").collect(JOINING_0));
+        assertEquals("12", Stream.of("1", "2").collect(JOINING_0));
+        assertEquals("123", Stream.of("1", "2", "3").collect(JOINING_0));
+        assertEquals("1null3", Stream.of("1", null, "3").collect(JOINING_0));
+    }
+
+    @Test
+    public void testJoiningStrings1Arg() {
+        assertEquals("", Stream.of().collect(JOINING_1));
+        assertEquals("1", Stream.of("1").collect(JOINING_1));
+        assertEquals("1-2", Stream.of("1", "2").collect(JOINING_1));
+        assertEquals("1-2-3", Stream.of("1", "2", "3").collect(JOINING_1));
+        assertEquals("1-null-3", Stream.of("1", null, "3").collect(JOINING_1));
+    }
+
+    @Test
+    public void testJoiningStrings3Args() {
+        assertEquals("<>", Stream.of().collect(JOINING_3));
+        assertEquals("<1>", Stream.of("1").collect(JOINING_3));
+        assertEquals("<1-2>", Stream.of("1", "2").collect(JOINING_3));
+        assertEquals("<1-2-3>", Stream.of("1", "2", "3").collect(JOINING_3));
+        assertEquals("<1-null-3>", Stream.of("1", null, "3").collect(JOINING_3));
+    }
+
+    @Test
+    public void testJoiningStrings4Args() {
+        assertEquals("<>", Stream.of().collect(JOINING_4));
+        assertEquals("<1>", Stream.of("1").collect(JOINING_4));
+        assertEquals("<1-2>", Stream.of("1", "2").collect(JOINING_4));
+        assertEquals("<1-2-3>", Stream.of("1", "2", "3").collect(JOINING_4));
+        assertEquals("<1-null-3>", Stream.of("1", null, "3").collect(JOINING_4));
+        assertEquals("<1-NUL-3>", Stream.of("1", null, "3").collect(JOINING_4_NUL));
+    }
+}


[commons-lang] 02/02: Sort members

Posted by gg...@apache.org.
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

commit 6a95f03a6a4c6ddfafcdca431b64603fb56ca189
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Aug 12 11:35:35 2022 -0400

    Sort members
---
 .../apache/commons/lang3/stream/StreamsTest.java   | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java b/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java
index e85957b3c..073e9cc71 100644
--- a/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java
+++ b/src/test/java/org/apache/commons/lang3/stream/StreamsTest.java
@@ -168,23 +168,12 @@ public class StreamsTest extends AbstractLangTest {
         assertEquals(2, Streams.of(Arrays.asList("A", "B")).collect(Collectors.toList()).size());
     }
 
-    @Test
-    public void testOfIterableNotNull() {
-        assertEquals(2, Streams.of((Iterable<String>) Arrays.asList("A", "B")).collect(Collectors.toList()).size());
-    }
-
     @Test
     public void testOfCollectionNull() {
         final List<String> input = null;
         assertEquals(0, Streams.of(input).collect(Collectors.toList()).size());
     }
 
-    @Test
-    public void testOfIterableNull() {
-        final Iterable<String> input = null;
-        assertEquals(0, Streams.of(input).collect(Collectors.toList()).size());
-    }
-
     @Test
     public void testOfEnumeration() {
         final Hashtable<String, Integer> table = new Hashtable<>();
@@ -199,6 +188,17 @@ public class StreamsTest extends AbstractLangTest {
         assertEquals(2, collect.size());
     }
 
+    @Test
+    public void testOfIterableNotNull() {
+        assertEquals(2, Streams.of((Iterable<String>) Arrays.asList("A", "B")).collect(Collectors.toList()).size());
+    }
+
+    @Test
+    public void testOfIterableNull() {
+        final Iterable<String> input = null;
+        assertEquals(0, Streams.of(input).collect(Collectors.toList()).size());
+    }
+
     @Test
     public void testSimpleStreamFilter() {
         final List<String> input = Arrays.asList("1", "2", "3", "4", "5", "6");