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 useful 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
096     * order.
097     * <p>
098     * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}.
099     * </p>
100     *
101     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter
102     *         order.
103     */
104    public static Collector<Object, ?, String> joining() {
105        return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID);
106    }
107
108    /**
109     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter
110     * order.
111     * <p>
112     * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just
113     * {@code CharSequence}.
114     * </p>
115     *
116     * @param delimiter the delimiter to be used between each element.
117     * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter
118     *         order.
119     */
120    public static Collector<Object, ?, String> joining(final CharSequence delimiter) {
121        return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY);
122    }
123
124    /**
125     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
126     * specified prefix and suffix, in encounter order.
127     * <p>
128     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
129     * element class, not just {@code CharSequence}.
130     * </p>
131     *
132     * @param delimiter the delimiter to be used between each element
133     * @param prefix the sequence of characters to be used at the beginning of the joined result
134     * @param suffix the sequence of characters to be used at the end of the joined result
135     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
136     *         encounter order
137     */
138    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
139        return joining(delimiter, prefix, suffix, Objects::toString);
140    }
141
142    /**
143     * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the
144     * specified prefix and suffix, in encounter order.
145     * <p>
146     * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any
147     * element class, not just {@code CharSequence}.
148     * </p>
149     *
150     * @param delimiter the delimiter to be used between each element
151     * @param prefix the sequence of characters to be used at the beginning of the joined result
152     * @param suffix the sequence of characters to be used at the end of the joined result
153     * @param toString A function that takes an Object and returns a non-null String.
154     * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in
155     *         encounter order
156     */
157    public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix,
158        final Function<Object, String> toString) {
159        return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge,
160            StringJoiner::toString, CH_NOID);
161    }
162
163    private LangCollectors() {
164        // No instance
165    }
166
167}