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 * http://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. 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 }