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 *      http://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.
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    public static <T, R, A> R collect(final Collector<? super T, A, R> collector, final T... array) {
111        return Arrays.stream(array).collect(collector);
112    }
113
114    /**
115     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
116     * <p>
117     * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
118     * </p>
119     * <p>
120     * For example:
121     * </p>
122     *
123     * <pre>
124     * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
125     *    .collect(LangCollectors.joining())
126     * returns "123"
127     * </pre>
128     *
129     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
130     */
131    public static Collector<Object, ?, String> joining() {
132        return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
133    }
134
135    /**
136     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
137     * <p>
138     * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just {@code CharSequence}.
139     * </p>
140     * <p>
141     * For example:
142     * </p>
143     *
144     * <pre>
145     * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
146     *   .collect(LangCollectors.joining("-"))
147     * returns "1-2-3"
148     * </pre>
149     *
150     * @param delimiter the delimiter to be used between each element.
151     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
152     */
153    public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
154        return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
155    }
156
157    /**
158     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
159     * specified prefix and suffix, in encounter order.
160     * <p>
161     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
162     * element class, not just {@code CharSequence}.
163     * </p>
164     * <p>
165     * For example:
166     * </p>
167     *
168     * <pre>
169     * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
170     *   .collect(LangCollectors.joining("-", "[", "]"))
171     * returns "[1-2-3]"
172     * </pre>
173     *
174     * @param delimiter the delimiter to be used between each element
175     * @param prefix the sequence of characters to be used at the beginning of the joined result
176     * @param suffix the sequence of characters to be used at the end of the joined result
177     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
178     *         encounter order
179     */
180    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
181        return joining(delimiter, prefix, suffix, Objects::toString);
182    }
183
184    /**
185     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in
186     * encounter order.
187     * <p>
188     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just
189     * {@code CharSequence}.
190     * </p>
191     * <p>
192     * For example:
193     * </p>
194     *
195     * <pre>{@code
196     * Stream.of(Long.valueOf(1), null, Long.valueOf(3))
197     *   .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL")))
198     * returns "[1-NUL-3]"
199     * }</pre>
200     *
201     * @param delimiter the delimiter to be used between each element
202     * @param prefix    the sequence of characters to be used at the beginning of the joined result
203     * @param suffix    the sequence of characters to be used at the end of the joined result
204     * @param toString  A function that takes an Object and returns a non-null String.
205     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order
206     */
207    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
208        final Function<Object, String> toString) {
209        return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
210            StringJoiner::toString, CH_NOID);
211    }
212
213    private LangCollectors() {
214        // No instance
215    }
216
217}