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.Collection; 017import java.util.List; 018import java.util.Map; 019import java.util.Objects; 020 021import java.util.concurrent.ConcurrentHashMap; 022 023import java.util.function.BiFunction; 024 025/** 026 * A notional list of elements from which sublists may be <dfn>selected</dfn> according to some <dfn>criteria</dfn>. 027 * 028 * @param <C> the type of criteria 029 * 030 * @param <T> the type of the elements 031 * 032 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 033 */ 034@FunctionalInterface 035public interface Selectable<C, T> { 036 037 /** 038 * <em>Selects</em> and returns an immutable {@link List} representing a sublist of this {@link Selectable}'s 039 * elements, as mediated by the supplied criteria. 040 * 041 * <p>Implementations of this method must be idempotent and must return a determinate value.</p> 042 * 043 * <p>Implementations of this method must not return {@code null}.</p> 044 * 045 * <p>Implementations of this method should not call {@link #list()}, since that method is typically implemented in 046 * terms of this one.</p> 047 * 048 * @param criteria the criteria to use; may be {@code null} 049 * 050 * @return an immutable sublist of this {@link Selectable}'s elements; never {@code null} 051 * 052 * @see #list() 053 */ 054 // Filters this thing according to the supplied criteria, producing a List. 055 // List not Stream to permit caching 056 // List not Collection so equals() is well-defined 057 // List is unmodifiable and is always valid for the supplied criteria (unenforceable) 058 // C and not Predicate because equality semantics for Predicate are not well-defined (caching again) 059 public List<T> select(final C criteria); 060 061 /** 062 * Returns an immutable {@link List} of all of this {@link Selectable}'s elements. 063 * 064 * <p>Implementations of this method must be idempotent and must return a determinate value.</p> 065 * 066 * <p>Implementations of this method must not return {@code null}.</p> 067 * 068 * <p>The default implementation of this method calls the {@link #select(Object)} method with {@code null} as the sole 069 * argument.</p> 070 * 071 * @return an immutable {@link List} of all of this {@link Selectable}'s elements; never {@code null} 072 * 073 * @see #select(Object) 074 */ 075 public default List<T> list() { 076 return this.select(null); 077 } 078 079 080 /* 081 * Static methods. 082 */ 083 084 085 /** 086 * Returns a {@link Selectable} using the supplied {@link Collection} as its elements, and the supplied {@link 087 * BiFunction} as its <em>selector function</em>. 088 * 089 * <p>There is no guarantee that this method will return new {@link Selectable} instances.</p> 090 * 091 * <p>The {@link Selectable} instances returned by this method may or may not cache their selections.</p> 092 * 093 * <p>The selector function must select a sublist from the supplied {@link Collection} as mediated by the supplied 094 * criteria. The selector function must additionally be idempotent and must produce a determinate value when given the 095 * same arguments.</p> 096 * 097 * <p>No validation of these semantics of the selector function is performed.</p> 098 * 099 * @param <C> the type of criteria 100 * 101 * @param <E> the type of the elements 102 * 103 * @param collection a {@link Collection} of elements from which sublists may be selected; must not be {@code null} 104 * 105 * @param f the selector function; must not be {@code null} 106 * 107 * @return a {@link Selectable}; never {@code null} 108 * 109 * @exception NullPointerException if either {@code collection} or {@code f} is {@code null} 110 */ 111 @SuppressWarnings("unchecked") 112 public static <C, E> Selectable<C, E> of(final Collection<? extends E> collection, final BiFunction<? super E, ? super C, ? extends Boolean> f) { 113 Objects.requireNonNull(f, "f"); 114 return collection.isEmpty() ? of() : c -> (List<E>)collection.stream() 115 .filter(e -> f.apply(e, c)) 116 .toList(); 117 } 118 119 /** 120 * Returns a {@link Selectable} whose {@link #select(Object)} method always returns an {@linkplain List#of() empty 121 * {@code List}}. 122 * 123 * @param <C> the type of criteria 124 * 125 * @param <E> the type of the elements 126 * 127 * @return a {@link Selectable} whose {@link #select(Object)} method always returns an {@linkplain List#of() empty 128 * {@code List}}; never {@code null} 129 */ 130 public static <C, E> Selectable<C, E> of() { 131 return c -> List.of(); 132 } 133 134 /** 135 * Returns a {@link Selectable} using the supplied {@link Collection} as its elements, and the supplied {@link 136 * BiFunction} as its <dfn>selector function</dfn>. 137 * 138 * <p>There is no guarantee that this method will return new {@link Selectable} instances.</p> 139 * 140 * <p>The {@link Selectable} instances returned by this method will cache their selections.</p> 141 * 142 * <p>The selector function must select a sublist from the supplied {@link Collection} as mediated by the supplied 143 * criteria. The selector function must additionally be idempotent and must produce a determinate value when given the 144 * same arguments.</p> 145 * 146 * <p>No validation of these semantics of the selector function is performed.</p> 147 * 148 * @param <C> the type of criteria 149 * 150 * @param <E> the type of the elements 151 * 152 * @param collection a {@link Collection} of elements from which sublists may be selected; must not be {@code null} 153 * 154 * @param f the selector function; must not be {@code null} 155 * 156 * @return a {@link Selectable}; never {@code null} 157 * 158 * @exception NullPointerException if either {@code collection} or {@code f} is {@code null} 159 */ 160 @SuppressWarnings("unchecked") 161 public static <C, E> Selectable<C, E> ofCaching(final Collection<? extends E> collection, final BiFunction<? super E, ? super C, ? extends Boolean> f) { 162 Objects.requireNonNull(f, "f"); 163 if (collection.isEmpty()) { 164 return c -> List.of(); 165 } 166 final Map<C, List<? extends E>> m = new ConcurrentHashMap<>(); 167 return c -> 168 (List<E>)m.computeIfAbsent(c, fc -> collection.stream() 169 .filter(e -> f.apply(e, fc)) 170 .toList()); 171 } 172 173}