All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.microbean.qualifier.Qualifiers Maven / Gradle / Ivy

/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
 *
 * Copyright © 2022 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.qualifier;

import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.TreeSet;

import java.util.function.Function;

import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;

import static org.microbean.constant.ConstantDescs.CD_Iterable;

import static org.microbean.qualifier.ConstantDescs.CD_Qualifiers;

/**
 * An immutable {@link Bindings} containing {@link Qualifier}
 * instances.
 *
 * 

This is a value-based * class.

* * @param the type of a {@link Qualifier}'s {@linkplain * Qualifier#attributes() attribute values} * * @author Laird Nelson * * @see Bindings */ public final class Qualifiers extends Bindings> { /* * Static fields. */ private static final Qualifiers EMPTY = new Qualifiers<>(List.of()); /* * Constructors. */ private Qualifiers(final Iterable> qualifiers) { super(qualifiers); } /* * Instance methods. */ /** * Returns a usually new {@link Qualifiers} with * this {@link Qualifiers}' entries and an additional entry * consisting of the supplied {@link Qualifier}. * *

The returned {@link Qualifiers} will be new * unless {@code qualifier} is {@code null}, in which case {@code * this} will be returned.

* * @param qualifier a {@link Qualifier}; may be {@code null} in * which case {@code this} will be returned * * @return a {@link Qualifiers} with this {@link Qualifiers}' * entries and an additional entry consisting of the supplied {@link * Qualifier} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. * * @see #plus(Iterable) */ public final Qualifiers plus(final Qualifier qualifier) { if (qualifier == null) { return this; } else if (this.isEmpty()) { return of(qualifier); } else { return this.plus(List.of(qualifier)); } } /** * Returns a usually new {@link Qualifiers} with * this {@link Qualifiers}' entries and additional entries * represented by the supplied {@code qualifiers}. * *

The returned {@link Qualifiers} will be new * unless {@code qualifier} is {@code null}, in which case {@code * this} will be returned.

* * @param qualifiers additional {@link Qualifier}s; may be {@code * null} in which case {@code this} will be returned * * @return a {@link Qualifiers} with this {@link Qualifiers}' * entries and additional entries represented by the supplied {@code * qualifiers} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ public final Qualifiers plus(final Iterable> qualifiers) { if (qualifiers == null) { return this; } else if (this.isEmpty()) { return of(qualifiers); } final Collection> newQualifiers = new TreeSet<>(); for (final Qualifier q : this) { newQualifiers.add(q); } for (final Qualifier q : qualifiers) { newQualifiers.add(q); } return of(newQualifiers); } /** * Returns a usually new {@link Qualifiers} whose * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute * keys} are prefixed with the supplied {@code prefix}. * *

If this {@link Qualifiers} is {@linkplain #isEmpty() empty}, * then {@code this} is returned.

* * @param prefix a prefix; if {@code null} then {@code this} will be returned * * @return a usually new {@link Qualifiers} whose * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute * keys} are prefixed with the supplied {@code prefix} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic, * assuming the supplied {@link Function} is. * * @threadsafety This method is safe for concurrent use by multiple * threads */ public final Qualifiers withPrefix(final String prefix) { if (prefix == null || this.isEmpty()) { return this; } return this.withPrefix(q -> prefix + q.name()); } /** * Returns a usually new {@link Qualifiers} whose * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute * keys} are produced by the supplied {@link Function}, which is * expected to prepend a prefix to the original key and return the * result. * *

If this {@link Qualifiers} is {@linkplain #isEmpty() empty}, * then {@code this} is returned.

* * @param f a deterministic, idempotent {@link Function} that * accepts keys drawn from this {@link Qualifiers}' {@link * Qualifier}s' {@linkplain Qualifier#attributes() attribute keys} * and returns a non-{@code null} prefixed version of that key; may * be {@code null} in which case {@code this} will be returned * * @return a usually new {@link Qualifiers} whose * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute * keys} have been prefixed by the actions of the supplied {@link * Function} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic, * assuming the supplied {@link Function} is. * * @threadsafety This method is safe for concurrent use by multiple * threads, assuming the supplied {@link Function} is */ public final Qualifiers withPrefix(final Function, ? extends String> f) { if (f == null || this.isEmpty()) { return this; } final Collection> newQualifiers = new TreeSet<>(); for (final Qualifier q : this) { newQualifiers.add(Qualifier.of(f.apply(q), q.value(), q.attributes(), q.info())); } return of(newQualifiers); } /** * Returns a {@link MethodHandleDesc} describing the constructor or * {@code static} method that will be used to create a dynamic * constant representing this {@link Qualifiers}. * * @return a {@link MethodHandleDesc} describing the constructor or * {@code static} method that will be used to create a dynamic * constant representing this {@link Qualifiers} * * @nullability This method does not, and its overrides must not, * return {@code null}. * * @idempotency This method is, and its overrides must be, * idempotent and deterministic. * * @threadsafety This method is, and its overrides must be, safe for * concurrent use by multiple threads. */ @Override // Bindings> protected final MethodHandleDesc describeConstructor() { return MethodHandleDesc.ofMethod(STATIC, CD_Qualifiers, "of", MethodTypeDesc.of(CD_Qualifiers, CD_Iterable)); } /* * Static methods. */ /** * Returns a {@link Qualifiers}, which may or may not be newly * created, whose {@link #isEmpty() isEmpty()} method will return * {@code true}. * * @param the type of the {@link Qualifier}'s {@linkplain * Qualifier#attributes() attribute values} * * @return a {@link Qualifiers} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ @SuppressWarnings("unchecked") public static final Qualifiers of() { return (Qualifiers)EMPTY; } /** * Returns a {@link Qualifiers}, which may or may not be newly * created, representing the supplied arguments. * * @param the type of the {@link Qualifier}'s {@linkplain * Qualifier#attributes() attribute values} * * @param qualifier the sole {@link Qualifier} the {@link * Qualifiers} will contain; must not be {@code null} * * @return a {@link Qualifiers} * * @exception NullPointerException if {@code qualifier} is {@code * null} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ public static final Qualifiers of(final Qualifier qualifier) { return of(List.of(Objects.requireNonNull(qualifier, "qualifier"))); } /** * Returns a {@link Qualifiers}, which may or may not be newly * created, representing the supplied arguments. * * @param the type of the {@link Qualifier}'s {@linkplain * Qualifier#attributes() attribute values} * * @param qualifier0 the first {@link Qualifier} the {@link * Qualifiers} will contain; must not be {@code null} * * @param qualifier1 the second {@link Qualifier} the {@link * Qualifiers} will contain; must not be {@code null} * * @return a {@link Qualifiers} * * @exception NullPointerException if {@code qualifier} is {@code * null} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ public static final Qualifiers of(final Qualifier qualifier0, final Qualifier qualifier1) { return of(List.of(qualifier0, qualifier1)); } /** * Returns a {@link Qualifiers}, which may or may not be newly * created, representing the supplied arguments. * * @param the type of the {@link Qualifier}'s {@linkplain * Qualifier#attributes() attribute values} * * @param qualifiers an {@link Iterable} representing {@link * Qualifier} instances the {@link Qualifiers} will contain; may be * {@code null} * * @return a {@link Qualifiers} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ public static final Qualifiers of(final Iterable> qualifiers) { if (qualifiers == null) { return of(); } final Iterator> i = qualifiers.iterator(); if (i.hasNext()) { final Collection> newQualifiers = new TreeSet<>(); newQualifiers.add(i.next()); while (i.hasNext()) { newQualifiers.add(i.next()); } return new Qualifiers<>(newQualifiers); } return of(); } /** * Returns a {@link Qualifiers}, which may or may not be newly * created, representing the supplied arguments. * * @param qualifier0 the first {@link Qualifier} the {@link * Qualifiers} will contain; must not be {@code null} * * @param qualifier1 the second {@link Qualifier} the {@link * Qualifiers} will contain; must not be {@code null} * * @return a {@link Qualifiers} * * @exception NullPointerException if {@code qualifier} is {@code * null} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ public static final Qualifiers ofDisparate(final Qualifier qualifier0, final Qualifier qualifier1) { return ofDisparate(List.of(qualifier0, qualifier1)); } /** * Returns a {@link Qualifiers}, which may or may not be newly * created, representing the supplied arguments. * * @param qualifiers an {@link Iterable} representing {@link * Qualifier} instances the {@link Qualifiers} will contain; may be * {@code null} * * @return a {@link Qualifiers} * * @nullability This method never returns {@code null}. * * @idempotency This method is idempotent and deterministic. * * @threadsafety This method is safe for concurrent use by multiple * threads. */ @SuppressWarnings("unchecked") public static final Qualifiers ofDisparate(final Iterable> qualifiers) { if (qualifiers == null) { return of(); } final Iterator> i = qualifiers.iterator(); if (i.hasNext()) { final Collection> newQualifiers = new TreeSet<>(); newQualifiers.add((Qualifier)i.next()); while (i.hasNext()) { newQualifiers.add((Qualifier)i.next()); } return new Qualifiers<>(newQualifiers); } return of(); } }