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; 017import java.util.Objects; 018 019import java.util.concurrent.ConcurrentHashMap; 020 021import java.util.function.BiFunction; 022import java.util.function.Function; 023 024/** 025 * A {@linkplain FunctionalInterface functional interface} whose implementations can <dfn>reduce</dfn> an unspecified 026 * notional collection of elements to a single element according to some <em>criteria</em>. 027 * 028 * <p>This interface is related to, but should not be confused with, the {@link Reducer} interface, implementations of 029 * which can be used to build {@link Reducible} instances.</p> 030 * 031 * @param <C> the type of criteria 032 * 033 * @param <T> the element type 034 * 035 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 036 * 037 * @see #reduce(Object) 038 * 039 * @see Reducer 040 */ 041@FunctionalInterface 042public interface Reducible<C, T> { 043 044 045 /* 046 * Instance methods. 047 */ 048 049 050 /** 051 * Given a criteria object, which may be {@code null}, returns an object that represents the <em>reduction</em> of a 052 * notional collection of objects. 053 * 054 * <p>Most {@link Reducible} implementations will return determinate values from invocations of this method, but there 055 * is no requirement to do so.</p> 056 * 057 * @param criteria the criteria; may be {@code null} to indicate no criteria 058 * 059 * @return a single object, or {@code null} 060 * 061 * @exception ReductionException if reduction could not occur or if an error occurs 062 */ 063 public T reduce(final C criteria); 064 065 066 /* 067 * Static methods. 068 */ 069 070 071 /** 072 * Calls the {@link #of(Selectable, Reducer, BiFunction)} method with the supplied {@code selectable}, the supplied {@code r}, and a reference to the {@link Reducer#fail(List, Object)} method, 073 * and returns the result. 074 * 075 * @param <C> the criteria type 076 * 077 * @param <E> the element type 078 * 079 * @param selectable a {@link Selectable}; must not be {@code null} 080 * 081 * @param r a {@link Reducer}; must not be {@code null} 082 * 083 * @return a {@link Reducible}; never {@code null} 084 * 085 * @exception NullPointerException if {@code selectable} or {@code r} is {@code null} 086 * 087 * @see #of(Selectable, Reducer, BiFunction) 088 * 089 * @see Reducer#fail(List, Object) 090 */ 091 public static <C, E> Reducible<C, E> of(final Selectable<C, E> selectable, 092 final Reducer<C, E> r) { 093 return of(selectable, r, Reducer::fail); 094 } 095 096 /** 097 * Returns a {@link Reducible} implementation that uses the supplied {@link Reducer} for its reduction operations. 098 * 099 * @param <C> the criteria type 100 * 101 * @param <E> the element type 102 * 103 * @param selectable a {@link Selectable}; must not be {@code null} 104 * 105 * @param r a {@link Reducer}; must not be {@code null} 106 * 107 * @param failureHandler a {@link BiFunction} serving as the supplied {@link Reducer}'s <dfn>failure handler</dfn>; 108 * must not be {@code null} 109 * 110 * @return a {@link Reducible}; never {@code null} 111 * 112 * @exception NullPointerException if any argument is {@code null} 113 * 114 * @see Reducer 115 */ 116 public static <C, E> Reducible<C, E> of(final Selectable<C, E> selectable, 117 final Reducer<C, E> r, 118 final BiFunction<? super List<? extends E>, ? super C, ? extends E> failureHandler) { 119 Objects.requireNonNull(selectable, "selectable"); 120 Objects.requireNonNull(r, "r"); 121 final BiFunction<? super List<? extends E>, ? super C, ? extends E> fh = Objects.requireNonNull(failureHandler, "failureHandler"); 122 return c -> r.reduce(selectable.select(c), c, fh); 123 } 124 125 /** 126 * Calls the {@link #ofCaching(Selectable, Reducer, BiFunction)} method with the supplied {@code selectable}, the 127 * supplied {@code r}, the supplied {@code failureHandler}, and a reference to the {@link Reducer#fail(List, Object)} 128 * method, and returns its result. 129 * 130 * @param <C> the criteria type 131 * 132 * @param <E> the element type 133 * 134 * @param selectable a {@link Selectable}; must not be {@code null} 135 * 136 * @param r a {@link Reducer}; must not be {@code null} 137 * 138 * @return a {@link Reducible}; never {@code null} 139 * 140 * @exception NullPointerException if any argument is {@code null} 141 * 142 * @see #ofCaching(Selectable, Reducer, BiFunction) 143 */ 144 public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable, final Reducer<C, E> r) { 145 return ofCaching(selectable, r, Reducer::fail); 146 } 147 148 /** 149 * Calls the {@link #ofCaching(Selectable, Reducer, BiFunction, BiFunction)} method with the supplied {@code 150 * selectable}, the supplied {@code r}, the supplied {@code fh}, and a reference to the {@link 151 * ConcurrentHashMap#computeIfAbsent(Object, java.util.function.Function) computeIfAbsent(Object, Function)} method of 152 * a new {@link ConcurrentHashMap}, and returns its result. 153 * 154 * @param <C> the criteria type 155 * 156 * @param <E> the element type 157 * 158 * @param selectable a {@link Selectable}; must not be {@code null} 159 * 160 * @param r a {@link Reducer}; must not be {@code null} 161 * 162 * @param fh a {@link BiFunction} serving as the supplied {@link Reducer}'s <dfn>failure handler</dfn>; 163 * must not be {@code null} 164 * 165 * @return a {@link Reducible}; never {@code null} 166 * 167 * @exception NullPointerException if any argument is {@code null} 168 * 169 * @see #ofCaching(Selectable, Reducer, BiFunction, BiFunction) 170 */ 171 public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable, 172 final Reducer<C, E> r, 173 final BiFunction<? super List<? extends E>, ? super C, ? extends E> fh) { 174 return ofCaching(selectable, r, fh, new ConcurrentHashMap<C, E>()::computeIfAbsent); 175 } 176 177 /** 178 * Returns a {@link Reducible} implementation that uses the supplied {@link Reducer} for its reduction operations and 179 * the supplied {@code computeIfAbsent} {@link BiFunction} for its caching implementation. 180 * 181 * @param <C> the criteria type 182 * 183 * @param <E> the element type 184 * 185 * @param selectable a {@link Selectable}; must not be {@code null} 186 * 187 * @param r a {@link Reducer}; must not be {@code null} 188 * 189 * @param fh a {@link BiFunction} serving as the supplied {@link Reducer}'s <dfn>failure handler</dfn>; must not be 190 * {@code null} 191 * 192 * @param cache a {@link BiFunction} with the same semantics as the {@link 193 * java.util.Map#computeIfAbsent(Object, java.util.function.Function)} method; must not be {@code null} 194 * 195 * @return a {@link Reducible}; never {@code null} 196 * 197 * @exception NullPointerException if any argument is {@code null} 198 * 199 * @see #of(Selectable, Reducer, BiFunction) 200 * 201 * @see java.util.Map#computeIfAbsent(Object, java.util.function.Function) 202 */ 203 public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable, 204 final Reducer<C, E> r, 205 final BiFunction<? super List<? extends E>, ? super C, ? extends E> fh, 206 final BiFunction<? super C, Function<C, E>, ? extends E> cache) { 207 Objects.requireNonNull(cache, "cache"); 208 final Reducible<C, E> reducible = of(selectable, r, fh); 209 return c -> cache.apply(c, reducible::reduce); 210 } 211 212}