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 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 order. 096 * <p> 097 * This is a variation of {@link Collectors#joining()} that works with any element class, not just {@code CharSequence}. 098 * </p> 099 * <p> 100 * For example: 101 * </p> 102 * 103 * <pre> 104 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3)) 105 * .collect(LangCollectors.joining()) 106 * returns "123" 107 * </pre> 108 * 109 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order. 110 */ 111 public static Collector<Object, ?, String> joining() { 112 return new SimpleCollector<>(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString, CH_NOID); 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(CharSequence)} 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 "1-2-3" 128 * </pre> 129 * 130 * @param delimiter the delimiter to be used between each element. 131 * @return A {@code Collector} which concatenates Object elements, separated by the specified delimiter, in encounter order. 132 */ 133 public static Collector<Object, ?, String> joining(final CharSequence delimiter) { 134 return joining(delimiter, StringUtils.EMPTY, StringUtils.EMPTY); 135 } 136 137 /** 138 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the 139 * specified prefix and suffix, in encounter order. 140 * <p> 141 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any 142 * element class, not just {@code CharSequence}. 143 * </p> 144 * <p> 145 * For example: 146 * </p> 147 * 148 * <pre> 149 * Stream.of(Long.valueOf(1), Long.valueOf(2), Long.valueOf(3)) 150 * .collect(LangCollectors.joining("-", "[", "]")) 151 * returns "[1-2-3]" 152 * </pre> 153 * 154 * @param delimiter the delimiter to be used between each element 155 * @param prefix the sequence of characters to be used at the beginning of the joined result 156 * @param suffix the sequence of characters to be used at the end of the joined result 157 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in 158 * encounter order 159 */ 160 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) { 161 return joining(delimiter, prefix, suffix, Objects::toString); 162 } 163 164 /** 165 * Returns a {@code Collector} that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix, in 166 * encounter order. 167 * <p> 168 * This is a variation of {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} that works with any element class, not just 169 * {@code CharSequence}. 170 * </p> 171 * <p> 172 * For example: 173 * </p> 174 * 175 * <pre>{@code 176 * Stream.of(Long.valueOf(1), null, Long.valueOf(3)) 177 * .collect(LangCollectors.joining("-", "[", "]", o -> Objects.toString(o, "NUL"))) 178 * returns "[1-NUL-3]" 179 * }</pre> 180 * 181 * @param delimiter the delimiter to be used between each element 182 * @param prefix the sequence of characters to be used at the beginning of the joined result 183 * @param suffix the sequence of characters to be used at the end of the joined result 184 * @param toString A function that takes an Object and returns a non-null String. 185 * @return A {@code Collector} which concatenates CharSequence elements, separated by the specified delimiter, in encounter order 186 */ 187 public static Collector<Object, ?, String> joining(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix, 188 final Function<Object, String> toString) { 189 return new SimpleCollector<>(() -> new StringJoiner(delimiter, prefix, suffix), (a, t) -> a.add(toString.apply(t)), StringJoiner::merge, 190 StringJoiner::toString, CH_NOID); 191 } 192 193 private LangCollectors() { 194 // No instance 195 } 196 197}