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}