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}