    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}