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.Collections;
021import java.util.Objects;
022import java.util.Set;
023import java.util.StringJoiner;
024import java.util.function.BiConsumer;
025import java.util.function.BinaryOperator;
026import java.util.function.Function;
027import java.util.function.Supplier;
028import java.util.stream.Collector;
029import java.util.stream.Collectors;
030
031import org.apache.commons.lang3.StringUtils;
032
033/**
034 * Implementations of {@link Collector} that implement various reduction operations.
035 * <p>
036 * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}.
037 * </p>
038 *
039 * @since 3.13.0
040 */
041public final class LangCollectors {
042
043    /**
044     * Simple implementation class for {@code Collector}.
045     *
046     * @param <T> the type of elements to be collected
047     * @param <R> the type of the result
048     */
049    private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> {
050
051        private final BiConsumer<A, T> accumulator;
052        private final Set<Characteristics> characteristics;
053        private final BinaryOperator<A> combiner;
054        private final Function<A, R> finisher;
055        private final Supplier<A> supplier;
056
057        private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher,
058            final Set<Characteristics> characteristics) {
059            this.supplier = supplier;
060            this.accumulator = accumulator;
061            this.combiner = combiner;
062            this.finisher = finisher;
063            this.characteristics = characteristics;
064        }
065
066        @Override
067        public BiConsumer<A, T> accumulator() {
068            return accumulator;
069        }
070
071        @Override
072        public Set<Characteristics> characteristics() {
073            return characteristics;
074        }
075
076        @Override
077        public BinaryOperator<A> combiner() {
078            return combiner;
079        }
080
081        @Override
082        public Function<A, R> finisher() {
083            return finisher;
084        }
085
086        @Override
087        public Supplier<A> supplier() {
088            return supplier;
089        }
090    }
091
092    private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
093
094    /**
095     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order.
096     * <p>
097     * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
098     * </p>
099     * <p>
100     * For example:
101     * </p>
102     *
103     * <pre>
104     * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
105     *    .collect(LangCollectors.joining())
106     * returns "123"
107     * </pre>
108     *
109     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
110     */
111    public static Collector<Object, ?, String> joining() {
112        return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
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(CharSequence)} 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 "1-2-3"
128     * </pre>
129     *
130     * @param delimiter the delimiter to be used between each element.
131     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order.
132     */
133    public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
134        return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
135    }
136
137    /**
138     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
139     * specified prefix and suffix, in encounter order.
140     * <p>
141     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
142     * element class, not just {@code CharSequence}.
143     * </p>
144     * <p>
145     * For example:
146     * </p>
147     *
148     * <pre>
149     * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3))
150     *   .collect(LangCollectors.joining("-", "[", "]"))
151     * returns "[1-2-3]"
152     * </pre>
153     *
154     * @param delimiter the delimiter to be used between each element
155     * @param prefix the sequence of characters to be used at the beginning of the joined result
156     * @param suffix the sequence of characters to be used at the end of the joined result
157     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
158     *         encounter order
159     */
160    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
161        return joining(delimiter, prefix, suffix, Objects::toString);
162    }
163
164    /**
165     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in
166     * encounter order.
167     * <p>
168     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just
169     * {@code CharSequence}.
170     * </p>
171     * <p>
172     * For example:
173     * </p>
174     *
175     * <pre>{@code
176     * Stream.of(Long.valueOf(1), null, Long.valueOf(3))
177     *   .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL")))
178     * returns "[1-NUL-3]"
179     * }</pre>
180     *
181     * @param delimiter the delimiter to be used between each element
182     * @param prefix    the sequence of characters to be used at the beginning of the joined result
183     * @param suffix    the sequence of characters to be used at the end of the joined result
184     * @param toString  A function that takes an Object and returns a non-null String.
185     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order
186     */
187    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
188        final Function<Object, String> toString) {
189        return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
190            StringJoiner::toString, CH_NOID);
191    }
192
193    private LangCollectors() {
194        // No instance
195    }
196
197}