    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}