1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.lang3.stream; 19 20 import java.util.Arrays; 21 import java.util.Collections; 22 import java.util.Objects; 23 import java.util.Set; 24 import java.util.StringJoiner; 25 import java.util.function.BiConsumer; 26 import java.util.function.BinaryOperator; 27 import java.util.function.Function; 28 import java.util.function.Supplier; 29 import java.util.stream.Collector; 30 import java.util.stream.Collectors; 31 import java.util.stream.Stream; 32 33 import org.apache.commons.lang3.StringUtils; 34 35 /** 36 * Implementations of {@link Collector} that implement various reduction operations. 37 * <p> 38 * This class is called {@code LangCollectors} instead of {@code Collectors} to avoid clashes with {@link Collectors}. 39 * </p> 40 * 41 * @since 3.13.0 42 */ 43 public final class LangCollectors { 44 45 /** 46 * Simple implementation class for {@code Collector}. 47 * 48 * @param <T> the type of elements to be collected 49 * @param <R> the type of the result 50 */ 51 private static final class SimpleCollector<T, A, R> implements Collector<T, A, R> { 52 53 private final BiConsumer<A, T> accumulator; 54 private final Set<Characteristics> characteristics; 55 private final BinaryOperator<A> combiner; 56 private final Function<A, R> finisher; 57 private final Supplier<A> supplier; 58 59 private SimpleCollector(final Supplier<A> supplier, final BiConsumer<A, T> accumulator, final BinaryOperator<A> combiner, final Function<A, R> finisher, 60 final Set<Characteristics> characteristics) { 61 this.supplier = supplier; 62 this.accumulator = accumulator; 63 this.combiner = combiner; 64 this.finisher = finisher; 65 this.characteristics = characteristics; 66 } 67 68 @Override 69 public BiConsumer<A, T> accumulator() { 70 return accumulator; 71 } 72 73 @Override 74 public Set<Characteristics> characteristics() { 75 return characteristics; 76 } 77 78 @Override 79 public BinaryOperator<A> combiner() { 80 return combiner; 81 } 82 83 @Override 84 public Function<A, R> finisher() { 85 return finisher; 86 } 87 88 @Override 89 public Supplier<A> supplier() { 90 return supplier; 91 } 92 } 93 94 private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet(); 95 96 /** 97 * Delegates to {@link Stream#collect(Collector)} for a Stream on the given array. 98 * 99 * @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, a null array treated as an empty array. 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 @SafeVarargs 111 public static <T, R, A> R collect(final Collector<? super T, A, R> collector, final T... array) { 112 return Streams.of(array).collect(collector); 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()} 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 "123" 128 * </pre> 129 * 130 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order. 131 */ 132 public static Collector<Object, ?, String> joining() { 133 return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID); 134 } 135 136 /** 137 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, in encounter order. 138 * <p> 139 * This is a variation of {@link Collectors#joining(CharSequence)} that works with any element class, not just {@code CharSequence}. 140 * </p> 141 * <p> 142 * For example: 143 * </p> 144 * 145 * <pre> 146 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3)) 147 * .collect(LangCollectors.joining("-")) 148 * returns "1-2-3" 149 * </pre> 150 * 151 * @param delimiter the delimiter to be used between each element. 152 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order. 153 */ 154 public static Collector<Object, ?, String> joining(final CharSequence delimiter) { 155 return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY); 156 } 157 158 /** 159 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the 160 * specified prefix and suffix, in encounter order. 161 * <p> 162 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any 163 * element class, not just {@code CharSequence}. 164 * </p> 165 * <p> 166 * For example: 167 * </p> 168 * 169 * <pre> 170 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3)) 171 * .collect(LangCollectors.joining("-", "[", "]")) 172 * returns "[1-2-3]" 173 * </pre> 174 * 175 * @param delimiter the delimiter to be used between each element 176 * @param prefix the sequence of characters to be used at the beginning of the joined result 177 * @param suffix the sequence of characters to be used at the end of the joined result 178 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in 179 * encounter order 180 */ 181 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) { 182 return joining(delimiter, prefix, suffix, Objects::toString); 183 } 184 185 /** 186 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in 187 * encounter order. 188 * <p> 189 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just 190 * {@code CharSequence}. 191 * </p> 192 * <p> 193 * For example: 194 * </p> 195 * 196 * <pre>{@code 197 * Stream.of(Long.valueOf(1), null, Long.valueOf(3)) 198 * .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL"))) 199 * returns "[1-NUL-3]" 200 * }</pre> 201 * 202 * @param delimiter the delimiter to be used between each element 203 * @param prefix the sequence of characters to be used at the beginning of the joined result 204 * @param suffix the sequence of characters to be used at the end of the joined result 205 * @param toString A function that takes an Object and returns a non-null String. 206 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order 207 */ 208 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix, 209 final Function<Object, String> toString) { 210 return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge, 211 StringJoiner::toString, CH_NOID); 212 } 213 214 private LangCollectors() { 215 // No instance 216 } 217 218 }