001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.lang3.stream;
019
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.Objects;
023import java.util.Set;
024import java.util.StringJoiner;
025import java.util.function.BiConsumer;
026import java.util.function.BinaryOperator;
027import java.util.function.Function;
028import java.util.function.Supplier;
029import java.util.stream.Collector;
030import java.util.stream.Collectors;
031import java.util.stream.Stream;
032
033import org.apache.commons.lang3.StringUtils;
034
035/**
036 * Implementations of {@link Collector} that implement various reduction operations.
037 * <p>
038 * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}.
039 * </p>
040 *
041 * @since 3.13.0
042 */
043public final class LangCollectors {
044
045    /**
046     * Simple implementation class for {@code Collector}.
047     *
048     * @param <T> the type of elements to be collected
049     * @param <R> the type of the result
050     */
051    private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> {
052
053        private final BiConsumer<A, T> accumulator;
054        private final Set<Characteristics> characteristics;
055        private final BinaryOperator<A> combiner;
056        private final Function<A, R> finisher;
057        private final Supplier<A> supplier;
058
059        private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher,
060            final Set<Characteristics> characteristics) {
061            this.supplier = supplier;
062            this.accumulator = accumulator;
063            this.combiner = combiner;
064            this.finisher = finisher;
065            this.characteristics = characteristics;
066        }
067
068        @Override
069        public BiConsumer<A, T> accumulator() {
070            return accumulator;
071        }
072
073        @Override
074        public Set<Characteristics> characteristics() {
075            return characteristics;
076        }
077
078        @Override
079        public BinaryOperator<A> combiner() {
080            return combiner;
081        }
082
083        @Override
084        public Function<A, R> finisher() {
085            return finisher;
086        }
087
088        @Override
089        public Supplier<A> supplier() {
090            return supplier;
091        }
092    }
093
094    private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
095
096    /**
097     * Delegates to {@link Stream#collect(Collector)} for a Stream on the given array.
098     *
099     * @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}