001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2025 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.bean; 015 016import java.util.List; 017 018import java.util.function.BiFunction; 019 020/** 021 * A {@linkplain FunctionalInterface functional interface} whose implementations can either <em>reduce</em> a supplied 022 * {@link List} of elements representing a successful <em>selection</em> to a single element normally drawn or 023 * calculated from the selection according to some <em>criteria</em>, or fail gracefully in the face of ambiguity by 024 * invoking a supplied <em>failure handler</em>. 025 * 026 * <p>The reduction may be a simple filtering operation, or may be a summing or aggregating operation, or anything 027 * else.</p> 028 * 029 * <p>This interface is conceptually subordinate to, but should not be confused with, the {@link Reducible} 030 * interface.</p> 031 * 032 * <p>{@link Reducer} implementations are often used to help build {@link Reducible} implementations. See, for example, 033 * {@link Reducible#ofCaching(Selectable, Reducer, BiFunction)}.</p> 034 * 035 * @param <C> the type of criteria 036 * 037 * @param <T> the element type 038 * 039 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 040 * 041 * @see #reduce(List, Object, BiFunction) 042 * 043 * @see Reducible 044 * 045 * @deprecated This interface is not really needed and is tentatively deprecated. 046 */ 047@Deprecated(since = "0.0.18") 048@FunctionalInterface 049public interface Reducer<C, T> { 050 051 /** 052 * Performs some kind of reductive or filtering operation on the supplied {@link List}, according to the supplied 053 * criteria, and returns the single result, or, if reduction fails, invokes the supplied {@link BiFunction} with a 054 * sublist representing a partial reduction (or an empty list representing a reduction that simply could not be 055 * performed), along with the supplied criteria, and returns its result. 056 * 057 * <p>Implementations of this method must return determinate values.</p> 058 * 059 * @param elements an immutable {@link List} to reduce; must not be {@code null}; represents a successful selection 060 * from a larger collection of elements 061 * 062 * @param c the criteria effectively describing the initial selection and the desired reduction; may be {@code null} 063 * to indicate no criteria; may be ignored if not needed by an implementation 064 * 065 * @param failureHandler a {@link BiFunction} receiving a failed reduction (usually a portion of the supplied {@code 066 * elements}), and the selection and reduction criteria, that returns a substitute reduction (or, more commonly, 067 * throws an exception); must not be {@code null}; must be invoked if reduction fails or undefined behavior may result 068 * 069 * @return a single, possibly {@code null}, element normally drawn or computed from the supplied {@code elements}, or 070 * a synthetic value returned by an invocation of the supplied {@code failureHandler}'s {@link 071 * BiFunction#apply(Object, Object) apply(Object, Object)} method 072 * 073 * @exception NullPointerException if {@code elements} or {@code failureHandler} is {@code null} 074 * 075 * @exception ReductionException if the {@code failureHandler} function throws a {@link ReductionException} 076 * 077 * @see #fail(List, Object) 078 */ 079 // List, not Stream, for equality semantics and caching purposes. 080 // List, not Set, because it's much faster and reduction can take care of duplicates if needed 081 // List, not Collection, because you want easy access to the (possibly) only element without creating iterators 082 // C, not Predicate, because it may not be necessary to actually filter the List to perform the reduction 083 // failureHandler will receive only those elements that could not be eliminated 084 // c is a pass-through used only during failure 085 public T reduce(final List<? extends T> elements, 086 final C c, 087 final BiFunction<? super List<? extends T>, ? super C, ? extends T> failureHandler); 088 089 090 /* 091 * Default methods. 092 */ 093 094 095 /** 096 * Invokes the {@link #reduce(List, Object, BiFunction)} method with the return value of an invocation of the {@link 097 * Selectable#select(Object) select(Object)} method on the supplied {@link Selectable} supplied with {@code c}, and 098 * the supplied {@code c} and {@code failureHandler} arguments, and returns the result. 099 * 100 * @param f a {@link Selectable}; must not be {@code null} 101 * 102 * @param c the criteria effectively describing the initial selection and the desired reduction; may be {@code null} 103 * to indicate no criteria; may be ignored if not needed by an implementation 104 * 105 * @param failureHandler a {@link BiFunction} receiving a failed reduction (usually a portion of the supplied {@code 106 * elements}), and the selection and reduction criteria, that returns a substitute reduction (or, more commonly, 107 * throws an exception); must not be {@code null}; must be invoked if reduction fails or undefined behavior may result 108 * 109 * @return a single, possibly {@code null}, element normally drawn or computed from the {@link List} returned from the 110 * supplied {@link Selectable}'s {@link Selectable#select(Object) select(Object)} method, or a synthetic value 111 * returned by an invocation of the supplied {@code failureHandler}'s {@link BiFunction#apply(Object, Object) 112 * apply(Object, Object)} method 113 * 114 * @exception NullPointerException if {@code f} or {@code failureHandler} is {@code null} 115 * 116 * @see #reduce(List, Object, BiFunction) 117 */ 118 public default T reduce(final Selectable<? super C, ? extends T> f, 119 final C c, 120 final BiFunction<? super List<? extends T>, ? super C, ? extends T> failureHandler) { 121 return this.reduce(f.select(c), c, failureHandler); 122 } 123 124 /** 125 * Invokes the {@link #reduce(List, Object, BiFunction)} method with the return value of an invocation of the {@link 126 * Selectable#select(Object) select(Object)} method on the supplied {@link Selectable} supplied with {@code c}, and 127 * the supplied {@code c} and a reference to the {@link #fail(List, Object) fail(List, Object)} method, and returns 128 * the result. 129 * 130 * @param f a {@link Selectable}; must not be {@code null} 131 * 132 * @param c the criteria effectively describing the initial selection and the desired reduction; may be {@code null} 133 * to indicate no criteria; may be ignored if not needed by an implementation 134 * 135 * @return a single, possibly {@code null}, element normally drawn or computed from the {@link List} returned from the 136 * supplied {@link Selectable}'s {@link Selectable#select(Object) select(Object)} method, or the sole element 137 * returned by an invocation of the {@link #fail(List, Object) fail(List, Object)} method 138 * 139 * @exception NullPointerException if {@code f} or {@code failureHandler} is {@code null} 140 * 141 * @exception UnsatisfiedReductionException if an invocation of the {@link #fail(List, Object) fail(List, Object)} method throws an {@link UnsatisfiedReductionException} 142 * 143 * @exception AmbiguousReductionException if an invocation of the {@link #fail(List, Object) fail(List, Object)} method throws an {@link AmbiguousReductionException} 144 * 145 * @see #reduce(Selectable, Object, BiFunction) 146 * 147 * @see #fail(List, Object) 148 */ 149 public default T reduce(final Selectable<? super C, ? extends T> f, final C c) { 150 return this.reduce(f, c, Reducer::fail); 151 } 152 153 /** 154 * Invokes the {@link #reduce(List, Object, BiFunction)} method with the supplied arguments and a reference to the 155 * {@link #fail(List, Object)} method, and returns the result. 156 * 157 * @param elements an immutable {@link List} to reduce; must not be {@code null}; represents a successful selection 158 * from a larger collection of elements 159 * 160 * @param c the criteria effectively describing the initial selection and the desired reduction; may be {@code null} 161 * to indicate no criteria; may be ignored if not needed by an implementation 162 * 163 * @return a single, possibly {@code null}, element normally drawn or computed from the supplied {@code elements}, or 164 * a synthetic value returned by an invocation of the supplied {@code failureHandler}'s {@link 165 * BiFunction#apply(Object, Object) apply(Object, Object)} method 166 * 167 * @exception NullPointerException if {@code elements} is {@code null} 168 * 169 * @see #fail(List, Object) 170 * 171 * @see #reduce(List, Object, BiFunction) 172 */ 173 public default T reduce(final List<? extends T> elements, final C c) { 174 return this.reduce(elements, c, Reducer::fail); 175 } 176 177 178 /* 179 * Static methods. 180 */ 181 182 183 /** 184 * Returns a {@link Reducer} whose {@link #reduce(List, Object, BiFunction)} method, for a given {@link List} of 185 * elements, returns the {@link List}'s sole element if the {@link List} has exactly one element, and returns the 186 * result of invoking its supplied failure handler otherwise. 187 * 188 * @param <C> the type of the criteria 189 * 190 * @param <T> the type of the elements 191 * 192 * @return a {@link Reducer}; never {@code null} 193 * 194 * @see #reduce(List, Object, BiFunction) 195 */ 196 // A Reducer that works only when the selection is of size 1. 197 public static <C, T> Reducer<C, T> ofSimple() { 198 return Reducer::reduceObviously; 199 } 200 201 /** 202 * Returns a {@link Reducer} whose {@link #reduce(List, Object, BiFunction)} method, for a given {@link List} of 203 * elements, returns the result of invoking its supplied failure handler. 204 * 205 * @param <C> the type of the criteria 206 * 207 * @param <T> the type of the elements 208 * 209 * @return a {@link Reducer}; never {@code null} 210 * 211 * @see #reduce(List, Object, BiFunction) 212 */ 213 // A Reducer that simply calls its supplied failure handler no matter what. 214 public static <C, T> Reducer<C, T> ofFailing() { 215 return Reducer::failUnconditionally; 216 } 217 218 /** 219 * Throws an {@link UnsatisfiedReductionException} if {@code elements} is empty, throws an {@link 220 * AmbiguousReductionException} if the {@linkplain List#size() size} of {@code elements} is greater than {@code 1}, 221 * and returns the sole element of {@code elements} otherwise. 222 * 223 * <p>A reference to this method is often used as a <dfn>failure handler</dfn> in an invocation of the {@link 224 * #reduce(List, Object, BiFunction)} method.</p> 225 * 226 * @param <C> the type of the criteria 227 * 228 * @param <T> the type of the elements 229 * 230 * @param elements a {@link List} of elements under reduction; must not be {@code null} 231 * 232 * @param c a criteria object; may be {@code null} 233 * 234 * @return the sole element present in {@code elements}, which may be {@code null} 235 * 236 * @exception NullPointerException if {@code elements} is {@code null} 237 * 238 * @exception UnsatisfiedReductionException if {@code elements} is empty 239 * 240 * @exception AmbiguousReductionException if the {@linkplain List#size() size} of {@code elements} is greater than 241 * {@code 1} 242 * 243 * @see #reduce(List, Object, BiFunction) 244 * 245 * @see #reduce(List, Object) 246 */ 247 // Default failure handler; call by method reference. Fails if the selection does not consist of one element. 248 public static <C, T> T fail(final List<? extends T> elements, final C c) { 249 return switch (elements.size()) { 250 case 0 -> throw new UnsatisfiedReductionException((Object)c, null, null); 251 case 1 -> elements.get(0); 252 default -> throw new AmbiguousReductionException(c, elements, "Cannot reduce: " + elements); 253 }; 254 } 255 256 /** 257 * Returns {@code null} when invoked, regardless of arguments. 258 * 259 * <p>A reference to this method can be used as a <dfn>failure handler</dfn> in an invocation of the {@link 260 * #reduce(List, Object, BiFunction)} method.</p> 261 * 262 * @param <A> the type of the first parameter; ignored 263 * 264 * @param <B> the type of the second parameter; ignored 265 * 266 * @param <C> the type of the return type; ignored 267 * 268 * @param a an object; may be {@code null}; ignored 269 * 270 * @param b an object; may be {@code null}; ignored 271 * 272 * @return {@code null} when invoked 273 */ 274 // Convenience failure handler; call by method reference. Returns null when invoked. 275 public static <A, B, C> C returnNull(final A a, final B b) { 276 return null; 277 } 278 279 private static <C, T> T reduceObviously(final List<? extends T> l, 280 final C c, 281 final BiFunction<? super List<? extends T>, ? super C, ? extends T> fh) { 282 return switch (l.size()) { 283 case 0 -> fh == null ? fail(List.of(), c) : fh.apply(List.of(), c); 284 case 1 -> l.get(0); 285 default -> fh == null ? fail(l, c) : fh.apply(l, c); 286 }; 287 } 288 289 private static <C, T> T failUnconditionally(final List<? extends T> l, 290 final C c, 291 final BiFunction<? super List<? extends T>, ? super C, ? extends T> fh) { 292 return fh.apply(l, c); 293 } 294 295}