001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024 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 reduce an unspecified notional
026 * 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, which can be used to
029 * 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   * Given a criteria object, which may be {@code null}, returns an object that represents the <em>reduction</em> of a
047   * notional collection of objects.
048   *
049   * <p>Most {@link Reducible} implementations will return determine values from invocations of this method, but there
050   * is no requirement to do so.</p>
051   *
052   * @param criteria the criteria; may be {@code null} to indicate no criteria
053   *
054   * @return a single object, or {@code null}
055   *
056   * @exception ReductionException if reduction could not occur or if an error occurs
057   */
058  public T reduce(final C criteria);
059
060
061  /*
062   * Static methods.
063   */
064
065
066  public static <C, E> Reducible<C, E> of(final Selectable<C, E> selectable,
067                                          final Reducer<C, E> r) {
068    return of(selectable, r, Reducer::fail);
069  }
070
071  public static <C, E> Reducible<C, E> of(final Selectable<C, E> selectable,
072                                          final Reducer<C, E> r,
073                                          final BiFunction<? super List<? extends E>, ? super C, ? extends E> failureHandler) {
074    Objects.requireNonNull(selectable, "selectable");
075    Objects.requireNonNull(r, "r");
076    final BiFunction<? super List<? extends E>, ? super C, ? extends E> fh = Objects.requireNonNull(failureHandler, "failureHandler");
077    return c -> r.reduce(selectable.select(c), c, fh);
078  }
079
080  public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable,
081                                                 final Reducer<C, E> r) {
082    return ofCaching(selectable, r, Reducer::fail);
083  }
084
085  public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable,
086                                                 final Reducer<C, E> r,
087                                                 final BiFunction<? super List<? extends E>, ? super C, ? extends E> failureHandler) {
088    return ofCaching(selectable, r, failureHandler, new ConcurrentHashMap<C, E>()::computeIfAbsent);
089  }
090
091  public static <C, E> Reducible<C, E> ofCaching(final Selectable<C, E> selectable,
092                                                 final Reducer<C, E> r,
093                                                 final BiFunction<? super List<? extends E>, ? super C, ? extends E> failureHandler,
094                                                 final BiFunction<? super C, Function<C, E>, ? extends E> computeIfAbsent) {
095    final Reducible<C, E> rd = of(selectable, r, failureHandler);
096    return c -> computeIfAbsent.apply(c, rd::reduce);
097  }
098
099}