Add LangCollectors
This commit is contained in:
parent
969a9d9f11
commit
62910e4f32
|
@ -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 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<E>).</action>
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Streams.of(Enumeration<E>).</action>
|
||||||
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Streams.of(Iterable<E>).</action>
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Streams.of(Iterable<E>).</action>
|
||||||
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add LangCollectors.</action>
|
||||||
<!-- UPDATE -->
|
<!-- 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, 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>
|
<action type="update" dev="ggregory" due-to="Dependabot">Bump actions/checkout from 2 to 3 #819, #825, #859.</action>
|
||||||
|
|
|
@ -27,12 +27,13 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringJoiner;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.function.Suppliers;
|
import org.apache.commons.lang3.function.Suppliers;
|
||||||
import org.apache.commons.lang3.function.ToBooleanBiFunction;
|
import org.apache.commons.lang3.function.ToBooleanBiFunction;
|
||||||
|
import org.apache.commons.lang3.stream.LangCollectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Operations on {@link java.lang.String} that are
|
* <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
|
* @return the joined String, {@code null} if null array input
|
||||||
*/
|
*/
|
||||||
public static String join(final Object[] array, final String delimiter) {
|
public static String join(final Object[] array, final String delimiter) {
|
||||||
if (array == null) {
|
return array != null ? join(array, toStringOrEmpty(delimiter), 0, array.length) : null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return join(array, delimiter, 0, array.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4747,17 +4745,8 @@ public class StringUtils {
|
||||||
* {@code endIndex > array.length()}
|
* {@code endIndex > array.length()}
|
||||||
*/
|
*/
|
||||||
public static String join(final Object[] array, final String delimiter, final int startIndex, final int endIndex) {
|
public static String join(final Object[] array, final String delimiter, final int startIndex, final int endIndex) {
|
||||||
if (array == null) {
|
return array != null ? Stream.of(array).skip(startIndex).limit(Math.max(0, endIndex - startIndex))
|
||||||
return null;
|
.collect(LangCollectors.joining(delimiter, EMPTY, EMPTY, StringUtils::toStringOrEmpty)) : 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue