From 9f8264aaaae26547d74e7af8a71fd2184f00f31d Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Fri, 13 Jun 2025 20:18:09 -0700 Subject: [PATCH] Hoists some constructs from microbean-bean into this project. Refreshes dependencies. Signed-off-by: Laird Nelson --- README.md | 4 +- pom.xml | 37 +++-- src/main/java/module-info.java | 1 + .../java/org/microbean/assign/Aggregate.java | 93 ++++++++++++ .../java/org/microbean/assign/Assignment.java | 45 ++++++ .../microbean/assign/AttributedElement.java | 102 +++++++++++++ .../org/microbean/assign/AttributedType.java | 142 ++++++++++++++++++ .../java/org/microbean/assign/Matcher.java | 3 +- .../java/org/microbean/assign/Selectable.java | 73 +++++++++ .../org/microbean/assign/Selectables.java | 140 +++++++++++++++++ 10 files changed, 621 insertions(+), 19 deletions(-) create mode 100644 src/main/java/org/microbean/assign/Aggregate.java create mode 100644 src/main/java/org/microbean/assign/Assignment.java create mode 100644 src/main/java/org/microbean/assign/AttributedElement.java create mode 100644 src/main/java/org/microbean/assign/AttributedType.java create mode 100644 src/main/java/org/microbean/assign/Selectable.java create mode 100644 src/main/java/org/microbean/assign/Selectables.java diff --git a/README.md b/README.md index 25b78ea..038e939 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ changes, regardless of project version and without notice.** # Requirements -microBean™ Assign requires a Java runtime of version 23 or higher. +microBean™ Assign requires a Java runtime of version 21 or higher. # Installation @@ -27,7 +27,7 @@ dependency: org.microbean microbean-assign - 0.0.5 + 0.0.6 ``` diff --git a/pom.xml b/pom.xml index ffc8cd1..8b6ddac 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ ${project.basedir}/src/test/java/logging.properties - 23 + 21 true true @@ -125,7 +125,7 @@ org.junit junit-bom - 5.11.3 + 5.13.1 pom import @@ -135,13 +135,19 @@ org.microbean microbean-attributes - 0.0.2 + 0.0.3 + + + + org.microbean + microbean-constant + 0.0.7 org.microbean microbean-construct - 0.0.10 + 0.0.11 @@ -154,7 +160,13 @@ microbean-attributes compile - + + + org.microbean + microbean-constant + compile + + org.microbean microbean-construct @@ -308,7 +320,7 @@ maven-clean-plugin - 3.4.1 + 3.5.0 @@ -423,14 +435,7 @@ maven-surefire-plugin - 3.5.2 - - - org.apache.maven.surefire - surefire-junit-platform - 3.5.2 - - + 3.5.3 maven-toolchains-plugin @@ -439,7 +444,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.1.0 + 4.9.3.0 org.codehaus.mojo @@ -449,7 +454,7 @@ io.smallrye jandex-maven-plugin - 3.2.7 + 3.3.1 org.sonatype.plugins diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index c419619..f3c90cb 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -23,6 +23,7 @@ requires transitive java.compiler; requires transitive org.microbean.attributes; + requires org.microbean.constant; requires transitive org.microbean.construct; } diff --git a/src/main/java/org/microbean/assign/Aggregate.java b/src/main/java/org/microbean/assign/Aggregate.java new file mode 100644 index 0000000..1723588 --- /dev/null +++ b/src/main/java/org/microbean/assign/Aggregate.java @@ -0,0 +1,93 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.util.Collection; +import java.util.Collections; +import java.util.SequencedSet; + +import java.util.function.Function; + +import static java.util.Collections.unmodifiableSequencedSet; + +import static java.util.LinkedHashSet.newLinkedHashSet; + +/** + * An object with {@linkplain AttributedElement dependencies}. + * + *

By default, {@link Aggregate}s have {@linkplain #EMPTY_DEPENDENCIES no dependencies}.

+ * + * @author Laird Nelson + * + * @see #dependencies() + */ +public interface Aggregate { + + + /* + * Static fields. + */ + + + /** + * An immutable, empty {@link SequencedSet} of {@link Assignment}s. + */ + public static final SequencedSet> EMPTY_ASSIGNMENTS = unmodifiableSequencedSet(newLinkedHashSet(0)); + + /** + * An immutable, empty {@link SequencedSet} of {@link AttributedElement}s. + */ + public static final SequencedSet EMPTY_DEPENDENCIES = unmodifiableSequencedSet(newLinkedHashSet(0)); + + + /* + * Default instance methods. + */ + + + /** + * Returns an immutable {@link SequencedSet} of {@link AttributedElement} instances. + * + * @return an immutable {@link SequencedSet} of {@link AttributedElement} instances; never {@code null} + * + * @see AttributedElement + */ + public default SequencedSet dependencies() { + return EMPTY_DEPENDENCIES; + } + + /** + * A convenience method that assigns a contextual reference to each of this {@link Aggregate}'s {@link + * AttributedElement} instances and returns the resulting {@link SequencedSet} of {@link Assignment}s. + * + *

Typically there is no need to override this method.

+ * + * @param r a {@link Function} that retrieves a contextual reference suitable for an {@link AttributedType}; if {@link + * #dependencies()} returns a non-empty {@link SequencedSet} then this argument must not be {@code null} + * + * @return an immutable {@link SequencedSet} of {@link Assignment} instances; never {@code null} + * + * @exception NullPointerException if {@code r} is {@code null} + */ + public default SequencedSet> assign(final Function r) { + final Collection ds = this.dependencies(); + if (ds == null || ds.isEmpty()) { + return EMPTY_ASSIGNMENTS; + } + final SequencedSet> assignments = newLinkedHashSet(ds.size()); + ds.forEach(d -> assignments.add(new Assignment<>(d, r.apply(d.attributedType())))); + return Collections.unmodifiableSequencedSet(assignments); + } + +} diff --git a/src/main/java/org/microbean/assign/Assignment.java b/src/main/java/org/microbean/assign/Assignment.java new file mode 100644 index 0000000..d0a42ce --- /dev/null +++ b/src/main/java/org/microbean/assign/Assignment.java @@ -0,0 +1,45 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.util.Objects; + +/** + * An assignment of a value to an {@link AttributedElement}. + * + * @param the value type + * + * @param assignee the {@link AttributedElement}; must not be {@code null} + * + * @param value the value; may be {@code null} + * + * @author Laird Nelson + */ +// You're going to be tempted to replace the value component with a Supplier component. Don't do it. An assignment is a +// value that belongs to, e.g., a field, so even if the value "came from" none/dependent/prototype scope, it was already +// sourced and "belongs to" the field. +public final record Assignment(AttributedElement assignee, R value) { + + /** + * Creates a new {@link Assignment}. + * + * @param assignee the {@link AttributedElement}; must not be {@code null} + * + * @param value the contextual reference; may be {@code null} + */ + public Assignment { + Objects.requireNonNull(assignee, "assignee"); + } + +} diff --git a/src/main/java/org/microbean/assign/AttributedElement.java b/src/main/java/org/microbean/assign/AttributedElement.java new file mode 100644 index 0000000..3df05d3 --- /dev/null +++ b/src/main/java/org/microbean/assign/AttributedElement.java @@ -0,0 +1,102 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.lang.constant.ClassDesc; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodHandleDesc; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import javax.lang.model.element.Element; + +import javax.lang.model.type.TypeMirror; + +import org.microbean.attributes.Attributed; +import org.microbean.attributes.Attributes; + +import org.microbean.constant.Constables; + +import static java.lang.constant.ConstantDescs.BSM_INVOKE; +import static java.lang.constant.ConstantDescs.CD_List; + +/** + * A pairing of an {@link Element} with a {@link List} of {@link Attributes}s. + * + * @param element an {@link Element} + * + * @param attributes a {@link List} of {@link Attributes}s + * + * @author Laird Nelson + */ +public final record AttributedElement(Element element, List attributes) implements Attributed, Constable { + + /** + * Creates a new {@link AttributedElement}. + * + * @param element a {@link Element}; must not be {@code null} + * + * @param attributes a {@link List} of {@link Attributes}; must not be {@code null} + * + * @exception NullPointerException if either argument is {@code null} + */ + public AttributedElement { + Objects.requireNonNull(element, "element"); + attributes = List.copyOf(attributes); + } + + /** + * Returns this {@link AttributedElement}'s {@linkplain Element#asType() type}. + * + * @return this {@link AttributedElement}'s {@linkplain Element#asType() type}; never {@code null} + */ + public final TypeMirror type() { + return this.element().asType(); + } + + /** + * Returns this {@link AttributedElement}'s {@link AttributedType}. + * + * @return this {@link AttributedElement}'s {@link AttributedType}; never {@code null} + * + * @see AttributedType + */ + public final AttributedType attributedType() { + return new AttributedType(this.type(), this.attributes()); + } + + /** + * Returns an {@link Optional} containing a {@link ConstantDesc} describing this {@link AttributedType}, or an + * {@linkplain Optional#isEmpty() empty Optional} if it could not be described. + * + * @return an {@link Optional} containing a {@link ConstantDesc} describing this {@link AttributedType}, or an + * {@linkplain Optional#isEmpty() empty Optional} if it could not be describe; never {@code null} + */ + @Override // Constable + public Optional describeConstable() { + return this.element() instanceof Constable e ? e.describeConstable() : Optional.empty() + .flatMap(elementDesc -> Constables.describeConstable(this.attributes()) + .map(attributesDesc -> DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), + ClassDesc.of("javax.lang.model.element.Element"), + CD_List), + elementDesc, + attributesDesc))); + } + +} diff --git a/src/main/java/org/microbean/assign/AttributedType.java b/src/main/java/org/microbean/assign/AttributedType.java new file mode 100644 index 0000000..2b11f3b --- /dev/null +++ b/src/main/java/org/microbean/assign/AttributedType.java @@ -0,0 +1,142 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.lang.constant.ClassDesc; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodHandleDesc; + +import java.util.List; +import java.util.Optional; + +import javax.lang.model.type.TypeMirror; + +import org.microbean.attributes.Attributed; +import org.microbean.attributes.Attributes; + +import org.microbean.constant.Constables; + +import static java.lang.constant.ConstantDescs.BSM_INVOKE; +import static java.lang.constant.ConstantDescs.CD_List; + +import static java.util.Arrays.asList; + +/** + * A pairing of a {@link TypeMirror} with a {@link List} of {@link Attributes}s. + * + * @param type a {@link TypeMirror} + * + * @param attributes a {@link List} of {@link Attributes}s + * + * @author Laird Nelson + */ +public final record AttributedType(TypeMirror type, List attributes) implements Attributed, Constable { + + /** + * Creates a new {@link AttributedType}. + * + * @param type a {@link TypeMirror}; must not be {@code null}; must be a {@linkplain + * javax.lang.model.type.TypeKind#isPrimitive() primitive}, {@linkplain javax.lang.model.type.TypeKind#ARRAY array} or + * {@linkplain javax.lang.model.type.TypeKind#DECLARED declared} type + * + * @param attributes an array of {@link Attributes}; may be {@code null} + * + * @exception NullPointerException if {@code type} is {@code null} + * + * @exception IllegalArgumentException if {@code type} is the wrong kind of type + */ + public AttributedType(final TypeMirror type, final Attributes... attributes) { + this(type, attributes == null || attributes.length <= 0 ? List.of() : asList(attributes)); + } + + /** + * Creates a new {@link AttributedType}. + * + * @param type a {@link TypeMirror}; must not be {@code null}; must be a {@linkplain + * javax.lang.model.type.TypeKind#isPrimitive() primitive}, {@linkplain javax.lang.model.type.TypeKind#ARRAY array} or + * {@linkplain javax.lang.model.type.TypeKind#DECLARED declared} type + * + * @param attributes a {@link List} of {@link Attributes}; must not be {@code null} + * + * @exception NullPointerException if either argument is {@code null} + * + * @exception IllegalArgumentException if {@code type} is the wrong kind of type + */ + public AttributedType { + switch (type.getKind()) { + case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT: + break; + case ERROR, EXECUTABLE, INTERSECTION, MODULE, NONE, NULL, OTHER, PACKAGE, TYPEVAR, UNION, VOID, WILDCARD: + default: + throw new IllegalArgumentException("type: " + type); + } + attributes = List.copyOf(attributes); + } + + /** + * Creates a new {@link AttributedType}. + * + * @param type a {@link TypeMirror}; must not be {@code null}; must be a {@linkplain + * javax.lang.model.type.TypeKind#isPrimitive() primitive}, {@linkplain javax.lang.model.type.TypeKind#ARRAY array} or + * {@linkplain javax.lang.model.type.TypeKind#DECLARED declared} type + * + * @exception NullPointerException if {@code type} is {@code null} + * + * @exception IllegalArgumentException if {@code type} is the wrong kind of type + */ + public AttributedType(final TypeMirror type) { + this(type, List.of()); + } + + /** + * Returns an {@link Optional} containing a {@link ConstantDesc} describing this {@link AttributedType}, or an + * {@linkplain Optional#isEmpty() empty Optional} if it could not be described. + * + * @return an {@link Optional} containing a {@link ConstantDesc} describing this {@link AttributedType}, or an + * {@linkplain Optional#isEmpty() empty Optional} if it could not be describe; never {@code null} + */ + @Override // Constable + public Optional describeConstable() { + return this.type() instanceof Constable t ? t.describeConstable() : Optional.empty() + .flatMap(typeDesc -> Constables.describeConstable(this.attributes()) + .map(attributesDesc -> DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), + ClassDesc.of(TypeMirror.class.getName()), + CD_List), + typeDesc, + attributesDesc))); + } + + /** + * Returns an {@link AttributedType} comprising the supplied arguments. + * + * @param type a {@link TypeMirror}; must not be {@code null}; must be a {@linkplain + * javax.lang.model.type.TypeKind#isPrimitive() primitive}, {@linkplain javax.lang.model.type.TypeKind#ARRAY array} or + * {@linkplain javax.lang.model.type.TypeKind#DECLARED declared} type + * + * @param attributes an array of {@link Attributes}; may be {@code null} + * + * @return a non-{@code null} {@link AttributedType} + * + * @exception NullPointerException if {@code type} is {@code null} + * + * @exception IllegalArgumentException if {@code type} is the wrong kind of type + */ + public static final AttributedType of(final TypeMirror type, Attributes... attributes) { + return new AttributedType(type, attributes == null || attributes.length <= 0 ? List.of() : asList(attributes)); + } + +} diff --git a/src/main/java/org/microbean/assign/Matcher.java b/src/main/java/org/microbean/assign/Matcher.java index f59be6b..2a17462 100644 --- a/src/main/java/org/microbean/assign/Matcher.java +++ b/src/main/java/org/microbean/assign/Matcher.java @@ -16,7 +16,8 @@ import java.util.function.BiPredicate; /** - * A {@link BiPredicate} with particular semantics associated with its {@link #test(Object, Object)} method. + * A {@link BiPredicate} with particular semantics associated with its {@link #test(Object, Object) test(Object, + * Object)} method. * * @param the criteria object * diff --git a/src/main/java/org/microbean/assign/Selectable.java b/src/main/java/org/microbean/assign/Selectable.java new file mode 100644 index 0000000..29ecbae --- /dev/null +++ b/src/main/java/org/microbean/assign/Selectable.java @@ -0,0 +1,73 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.util.List; + +/** + * A notional list of elements from which immutable sublists may be selected according to some + * criteria. + * + * @param the criteria type + * + * @param the element type + * + * @author Laird Nelson + */ +@FunctionalInterface +public interface Selectable { + + /** + * Selects and returns an immutable {@link List} representing a sublist of this {@link Selectable}'s + * elements, as mediated by the supplied criteria. + * + *

Implementations of this method must be idempotent and must return a determinate value.

+ * + *

Implementations of this method must not return {@code null}.

+ * + *

Implementations of this method should not call {@link #list()}, since that method is typically implemented in + * terms of this one, or undefined behavior may result.

+ * + * @param criteria the criteria to use; may be {@code null} + * + * @return an immutable sublist of this {@link Selectable}'s elements; never {@code null} + * + * @see #list() + */ + // Filters this thing according to the supplied criteria, producing a List. + // List not Stream to permit caching + // List not Collection so equals() is well-defined + // List is unmodifiable and is always valid for the supplied criteria (unenforceable) + // C and not Predicate because equality semantics for Predicate are not well-defined (caching again) + public List select(final C criteria); + + /** + * Returns an immutable {@link List} of all of this {@link Selectable}'s elements. + * + *

Implementations of this method must be idempotent and must return a determinate value.

+ * + *

Implementations of this method must not return {@code null}.

+ * + *

The default implementation of this method calls the {@link #select(Object)} method with {@code null} as the sole + * argument.

+ * + * @return an immutable {@link List} of all of this {@link Selectable}'s elements; never {@code null} + * + * @see #select(Object) + */ + public default List list() { + return this.select(null); + } + +} diff --git a/src/main/java/org/microbean/assign/Selectables.java b/src/main/java/org/microbean/assign/Selectables.java new file mode 100644 index 0000000..040ca6a --- /dev/null +++ b/src/main/java/org/microbean/assign/Selectables.java @@ -0,0 +1,140 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2025 microBean™. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.microbean.assign; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import java.util.concurrent.ConcurrentHashMap; + +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; + +/** + * Utility methods for working with {@link Selectable}s. + * + * @author Laird Nelson + * + * @see Selectable + */ +public final class Selectables { + + private Selectables() { + super(); + } + + /** + * Returns a {@link Selectable} that caches its results. + * + *

The cache is unbounded.

+ * + * @param the criteria type + * + * @param the element type + * + * @param selectable a {@link Selectable}; must not be {@code null} + * + * @return a non-{@code null} {@link Selectable} + * + * @exception NullPointerException if {@code selectable} is {@code null} + * + * @see #caching(Selectable, BiFunction) + */ + public static Selectable caching(final Selectable selectable) { + final Map> selectionCache = new ConcurrentHashMap<>(); + return Selectables.caching(selectable, selectionCache::computeIfAbsent); + } + + /** + * Returns a {@link Selectable} that caches its results. + * + * @param the criteria type + * + * @param the element type + * + * @param selectable a {@link Selectable}; must not be {@code null} + * + * @param f a {@link BiFunction} that returns a cached result, computing it on demand via its supplied mapping {@link + * Function} if necessary; must not be {@code null}; normally safe for concurrent use by multiple threads; often a + * reference to the {@link ConcurrentHashMap#computeIfAbsent(Object, Function)} method + * + * @return a non-{@code null} {@link Selectable} + * + * @exception NullPointerException if {@code selectable} or {@code f} is {@code null} + * + * @see ConcurrentHashMap#computeIfAbsent(Object, Function) + */ + public static Selectable caching(final Selectable selectable, + final BiFunction>, ? extends List> f) { + return c -> f.apply(c, selectable::select); + } + + /** + * Returns a {@link Selectable} whose {@link Selectable#select(Object)} method always returns an {@linkplain List#of() + * empty, immutable List}. + * + *

This method is useful primarily for completeness and for testing pathological situations.

+ * + * @param the criteria type + * + * @param the element type + * + * @return a non-{@code null} {@link Selectable} + */ + public static final Selectable empty() { + return Selectables::empty; + } + + private static final List empty(final C ignored) { + return List.of(); + } + + /** + * Returns a {@link Selectable} using the supplied {@link Collection} as its elements, and the supplied {@link + * BiPredicate} as its selector. + * + *

There is no guarantee that this method will return new {@link Selectable} instances.

+ * + *

The {@link Selectable} instances returned by this method may or may not cache their selections.

+ * + *

The selector must (indirectly) designate a sublist from the supplied {@link Collection} as mediated by the + * supplied criteria. The selector must additionally be idempotent and must produce a determinate value when given the + * same arguments.

+ * + *

No validation of these semantics of the selector is performed.

+ * + * @param the criteria type + * + * @param the element type + * + * @param collection a {@link Collection} of elements from which sublists may be selected; must not be {@code null} + * + * @param p the selector; must not be {@code null} + * + * @return a {@link Selectable}; never {@code null} + * + * @exception NullPointerException if either {@code collection} or {@code p} is {@code null} + */ + @SuppressWarnings("unchecked") + public static Selectable filtering(final Collection collection, + final BiPredicate p) { + Objects.requireNonNull(p, "p"); + return collection.isEmpty() ? empty() : c -> (List)collection.stream().filter(e -> p.test(e, c)).toList(); + } + + +}