View Javadoc
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 }