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}