1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.lang3.stream;
19
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Objects;
23 import java.util.Set;
24 import java.util.StringJoiner;
25 import java.util.function.BiConsumer;
26 import java.util.function.BinaryOperator;
27 import java.util.function.Function;
28 import java.util.function.Supplier;
29 import java.util.stream.Collector;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import org.apache.commons.lang3.StringUtils;
34
35 /**
36 * Implementations of {@link Collector} that implement various reduction operations.
37 * <p>
38 * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}.
39 * </p>
40 *
41 * @since 3.13.0
42 */
43 public final class LangCollectors {
44
45 /**
46 * Simple implementation class for {@code Collector}.
47 *
48 * @param <T> the type of elements to be collected
49 * @param <R> the type of the result
50 */
51 private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> {
52
53 private final BiConsumer<A, T> accumulator;
54 private final Set<Characteristics> characteristics;
55 private final BinaryOperator<A> combiner;
56 private final Function<A, R> finisher;
57 private final Supplier<A> supplier;
58
59 private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher,
60 final Set<Characteristics> characteristics) {
61 this.supplier = supplier;
62 this.accumulator = accumulator;
63 this.combiner = combiner;
64 this.finisher = finisher;
65 this.characteristics = characteristics;
66 }
67
68 @Override
69 public BiConsumer<A, T> accumulator() {
70 return accumulator;
71 }
72
73 @Override
74 public Set<Characteristics> characteristics() {
75 return characteristics;
76 }
77
78 @Override
79 public BinaryOperator<A> combiner() {
80 return combiner;
81 }
82
83 @Override
84 public Function<A, R> finisher() {
85 return finisher;
86 }
87
88 @Override
89 public Supplier<A> supplier() {
90 return supplier;
91 }
92 }
93
94 private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
95
96 /**
97 * Delegates to {@link Stream#collect(Collector)} for a Stream on the given array.
98 *
99 * @param <T> The type of the array elements.
100 * @param <R> the type of the result.
101 * @param <A> the intermediate accumulation type of the {@code Collector}.
102 * @param collector the {@code Collector} describing the reduction.
103 * @param array The array, assumed to be unmodified during use, a null array treated as an empty array.
104 * @return the result of the reduction
105 * @see Stream#collect(Collector)
106 * @see Arrays#stream(Object[])
107 * @see Collectors
108 * @since 3.16.0
109 */
110 @SafeVarargs
111 public static <T, R, A> R collect(final Collector<? super T, A, R> collector, final T... array) {
112 return Streams.of(array).collect(collector);
113 }
114
115 /**
116 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
117 * <p>
118 * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
119 * </p>
120 * <p>
121 * For example:
122 * </p>
123 *
124 * <pre>
125 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
126 * .collect(LangCollectors.joining())
127 * returns "123"
128 * </pre>
129 *
130 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
131 */
132 public static Collector<Object, ?, String> joining() {
133 return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
134 }
135
136 /**
137 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
138 * <p>
139 * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just {@code CharSequence}.
140 * </p>
141 * <p>
142 * For example:
143 * </p>
144 *
145 * <pre>
146 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
147 * .collect(LangCollectors.joining("-"))
148 * returns "1-2-3"
149 * </pre>
150 *
151 * @param delimiter the delimiter to be used between each element.
152 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
153 */
154 public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
155 return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
156 }
157
158 /**
159 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
160 * specified prefix and suffix, in encounter order.
161 * <p>
162 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
163 * element class, not just {@code CharSequence}.
164 * </p>
165 * <p>
166 * For example:
167 * </p>
168 *
169 * <pre>
170 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
171 * .collect(LangCollectors.joining("-", "[", "]"))
172 * returns "[1-2-3]"
173 * </pre>
174 *
175 * @param delimiter the delimiter to be used between each element
176 * @param prefix the sequence of characters to be used at the beginning of the joined result
177 * @param suffix the sequence of characters to be used at the end of the joined result
178 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
179 * encounter order
180 */
181 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
182 return joining(delimiter, prefix, suffix, Objects::toString);
183 }
184
185 /**
186 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in
187 * encounter order.
188 * <p>
189 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just
190 * {@code CharSequence}.
191 * </p>
192 * <p>
193 * For example:
194 * </p>
195 *
196 * <pre>{@code
197 * Stream.of(Long.valueOf(1), null, Long.valueOf(3))
198 * .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL")))
199 * returns "[1-NUL-3]"
200 * }</pre>
201 *
202 * @param delimiter the delimiter to be used between each element
203 * @param prefix the sequence of characters to be used at the beginning of the joined result
204 * @param suffix the sequence of characters to be used at the end of the joined result
205 * @param toString A function that takes an Object and returns a non-null String.
206 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order
207 */
208 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
209 final Function<Object, String> toString) {
210 return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
211 StringJoiner::toString, CH_NOID);
212 }
213
214 private LangCollectors() {
215 // No instance
216 }
217
218 }