001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2022 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.qualifier;
018
019import java.lang.constant.MethodHandleDesc;
020import java.lang.constant.MethodTypeDesc;
021
022import java.util.Collection;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Objects;
026import java.util.TreeSet;
027
028import java.util.function.Function;
029
030import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;
031
032import static org.microbean.constant.ConstantDescs.CD_Iterable;
033
034import static org.microbean.qualifier.ConstantDescs.CD_Qualifiers;
035
036/**
037 * An immutable {@link Bindings} containing {@link Qualifier}
038 * instances.
039 *
040 * <p>This is a <a
041 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
042 * class.</p>
043 *
044 * @param <V> the type of a {@link Qualifier}'s {@linkplain
045 * Qualifier#attributes() attribute values}
046 *
047 * @author <a href="https://about.me/lairdnelson"
048 * target="_parent">Laird Nelson</a>
049 *
050 * @see Bindings
051 */
052public final class Qualifiers<V> extends Bindings<V, Qualifier<V>> {
053
054
055  /*
056   * Static fields.
057   */
058
059
060  private static final Qualifiers<?> EMPTY = new Qualifiers<>(List.of());
061
062
063  /*
064   * Constructors.
065   */
066
067
068  private Qualifiers(final Iterable<? extends Qualifier<V>> qualifiers) {
069    super(qualifiers);
070  }
071
072
073  /*
074   * Instance methods.
075   */
076
077
078  /**
079   * Returns a <strong>usually new</strong> {@link Qualifiers} with
080   * this {@link Qualifiers}' entries and an additional entry
081   * consisting of the supplied {@link Qualifier}.
082   *
083   * <p>The returned {@link Qualifiers} <strong>will be new</strong>
084   * unless {@code qualifier} is {@code null}, in which case {@code
085   * this} will be returned.</p>
086   *
087   * @param qualifier a {@link Qualifier}; may be {@code null} in
088   * which case {@code this} will be returned
089   *
090   * @return a {@link Qualifiers} with this {@link Qualifiers}'
091   * entries and an additional entry consisting of the supplied {@link
092   * Qualifier}
093   *
094   * @nullability This method never returns {@code null}.
095   *
096   * @idempotency This method is idempotent and deterministic.
097   *
098   * @threadsafety This method is safe for concurrent use by multiple
099   * threads.
100   *
101   * @see #plus(Iterable)
102   */
103  public final Qualifiers<V> plus(final Qualifier<V> qualifier) {
104    if (qualifier == null) {
105      return this;
106    } else if (this.isEmpty()) {
107      return of(qualifier);
108    } else {
109      return this.plus(List.of(qualifier));
110    }
111  }
112
113  /**
114   * Returns a <strong>usually new</strong> {@link Qualifiers} with
115   * this {@link Qualifiers}' entries and additional entries
116   * represented by the supplied {@code qualifiers}.
117   *
118   * <p>The returned {@link Qualifiers} <strong>will be new</strong>
119   * unless {@code qualifier} is {@code null}, in which case {@code
120   * this} will be returned.</p>
121   *
122   * @param qualifiers additional {@link Qualifier}s; may be {@code
123   * null} in which case {@code this} will be returned
124   *
125   * @return a {@link Qualifiers} with this {@link Qualifiers}'
126   * entries and additional entries represented by the supplied {@code
127   * qualifiers}
128   *
129   * @nullability This method never returns {@code null}.
130   *
131   * @idempotency This method is idempotent and deterministic.
132   *
133   * @threadsafety This method is safe for concurrent use by multiple
134   * threads.
135   */
136  public final Qualifiers<V> plus(final Iterable<? extends Qualifier<V>> qualifiers) {
137    if (qualifiers == null) {
138      return this;
139    } else if (this.isEmpty()) {
140      return of(qualifiers);
141    }
142    final Collection<Qualifier<V>> newQualifiers = new TreeSet<>();
143    for (final Qualifier<V> q : this) {
144      newQualifiers.add(q);
145    }
146    for (final Qualifier<V> q : qualifiers) {
147      newQualifiers.add(q);
148    }
149    return of(newQualifiers);
150  }
151
152  /**
153   * Returns a <strong>usually new</strong> {@link Qualifiers} whose
154   * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute
155   * keys} are prefixed with the supplied {@code prefix}.
156   *
157   * <p>If this {@link Qualifiers} is {@linkplain #isEmpty() empty},
158   * then {@code this} is returned.</p>
159   *
160   * @param prefix a prefix; if {@code null} then {@code this} will be returned
161   *
162   * @return a <strong>usually new</strong> {@link Qualifiers} whose
163   * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute
164   * keys} are prefixed with the supplied {@code prefix}
165   *
166   * @nullability This method never returns {@code null}.
167   *
168   * @idempotency This method is idempotent and deterministic,
169   * assuming the supplied {@link Function} is.
170   *
171   * @threadsafety This method is safe for concurrent use by multiple
172   * threads
173   */
174  public final Qualifiers<V> withPrefix(final String prefix) {
175    if (prefix == null || this.isEmpty()) {
176      return this;
177    }
178    return this.withPrefix(q -> prefix + q.name());
179  }
180
181  /**
182   * Returns a <strong>usually new</strong> {@link Qualifiers} whose
183   * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute
184   * keys} are produced by the supplied {@link Function}, which is
185   * expected to prepend a prefix to the original key and return the
186   * result.
187   *
188   * <p>If this {@link Qualifiers} is {@linkplain #isEmpty() empty},
189   * then {@code this} is returned.</p>
190   *
191   * @param f a deterministic, idempotent {@link Function} that
192   * accepts keys drawn from this {@link Qualifiers}' {@link
193   * Qualifier}s' {@linkplain Qualifier#attributes() attribute keys}
194   * and returns a non-{@code null} prefixed version of that key; may
195   * be {@code null} in which case {@code this} will be returned
196   *
197   * @return a <strong>usually new</strong> {@link Qualifiers} whose
198   * {@link Qualifier}s' {@linkplain Qualifier#attributes() attribute
199   * keys} have been prefixed by the actions of the supplied {@link
200   * Function}
201   *
202   * @nullability This method never returns {@code null}.
203   *
204   * @idempotency This method is idempotent and deterministic,
205   * assuming the supplied {@link Function} is.
206   *
207   * @threadsafety This method is safe for concurrent use by multiple
208   * threads, assuming the supplied {@link Function} is
209   */
210  public final Qualifiers<V> withPrefix(final Function<? super Qualifier<V>, ? extends String> f) {
211    if (f == null || this.isEmpty()) {
212      return this;
213    }
214    final Collection<Qualifier<V>> newQualifiers = new TreeSet<>();
215    for (final Qualifier<V> q : this) {
216      newQualifiers.add(Qualifier.of(f.apply(q),
217                                     q.value(),
218                                     q.attributes(),
219                                     q.info()));
220    }
221    return of(newQualifiers);
222  }
223
224  /**
225   * Returns a {@link MethodHandleDesc} describing the constructor or
226   * {@code static} method that will be used to create a <a
227   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
228   * constant</a> representing this {@link Qualifiers}.
229   *
230   * @return a {@link MethodHandleDesc} describing the constructor or
231   * {@code static} method that will be used to create a <a
232   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
233   * constant</a> representing this {@link Qualifiers}
234   *
235   * @nullability This method does not, and its overrides must not,
236   * return {@code null}.
237   *
238   * @idempotency This method is, and its overrides must be,
239   * idempotent and deterministic.
240   *
241   * @threadsafety This method is, and its overrides must be, safe for
242   * concurrent use by multiple threads.
243   */
244  @Override // Bindings<V, Qualifier<V>>
245  protected final MethodHandleDesc describeConstructor() {
246    return
247      MethodHandleDesc.ofMethod(STATIC,
248                                CD_Qualifiers,
249                                "of",
250                                MethodTypeDesc.of(CD_Qualifiers, CD_Iterable));
251  }
252
253
254  /*
255   * Static methods.
256   */
257
258
259  /**
260   * Returns a {@link Qualifiers}, which may or may not be newly
261   * created, whose {@link #isEmpty() isEmpty()} method will return
262   * {@code true}.
263   *
264   * @param <V> the type of the {@link Qualifier}'s {@linkplain
265   * Qualifier#attributes() attribute values}
266   *
267   * @return a {@link Qualifiers}
268   *
269   * @nullability This method never returns {@code null}.
270   *
271   * @idempotency This method is idempotent and deterministic.
272   *
273   * @threadsafety This method is safe for concurrent use by multiple
274   * threads.
275   */
276  @SuppressWarnings("unchecked")
277  public static final <V> Qualifiers<V> of() {
278    return (Qualifiers<V>)EMPTY;
279  }
280
281  /**
282   * Returns a {@link Qualifiers}, which may or may not be newly
283   * created, representing the supplied arguments.
284   *
285   * @param <V> the type of the {@link Qualifier}'s {@linkplain
286   * Qualifier#attributes() attribute values}
287   *
288   * @param qualifier the sole {@link Qualifier} the {@link
289   * Qualifiers} will contain; must not be {@code null}
290   *
291   * @return a {@link Qualifiers}
292   *
293   * @exception NullPointerException if {@code qualifier} is {@code
294   * null}
295   *
296   * @nullability This method never returns {@code null}.
297   *
298   * @idempotency This method is idempotent and deterministic.
299   *
300   * @threadsafety This method is safe for concurrent use by multiple
301   * threads.
302   */
303  public static final <V> Qualifiers<V> of(final Qualifier<V> qualifier) {
304    return of(List.of(Objects.requireNonNull(qualifier, "qualifier")));
305  }
306
307  /**
308   * Returns a {@link Qualifiers}, which may or may not be newly
309   * created, representing the supplied arguments.
310   *
311   * @param <V> the type of the {@link Qualifier}'s {@linkplain
312   * Qualifier#attributes() attribute values}
313   *
314   * @param qualifier0 the first {@link Qualifier} the {@link
315   * Qualifiers} will contain; must not be {@code null}
316   *
317   * @param qualifier1 the second {@link Qualifier} the {@link
318   * Qualifiers} will contain; must not be {@code null}
319   *
320   * @return a {@link Qualifiers}
321   *
322   * @exception NullPointerException if {@code qualifier} is {@code
323   * null}
324   *
325   * @nullability This method never returns {@code null}.
326   *
327   * @idempotency This method is idempotent and deterministic.
328   *
329   * @threadsafety This method is safe for concurrent use by multiple
330   * threads.
331   */
332  public static final <V> Qualifiers<V> of(final Qualifier<V> qualifier0, final Qualifier<V> qualifier1) {
333    return of(List.of(qualifier0, qualifier1));
334  }
335
336  /**
337   * Returns a {@link Qualifiers}, which may or may not be newly
338   * created, representing the supplied arguments.
339   *
340   * @param <V> the type of the {@link Qualifier}'s {@linkplain
341   * Qualifier#attributes() attribute values}
342   *
343   * @param qualifiers an {@link Iterable} representing {@link
344   * Qualifier} instances the {@link Qualifiers} will contain; may be
345   * {@code null}
346   *
347   * @return a {@link Qualifiers}
348   *
349   * @nullability This method never returns {@code null}.
350   *
351   * @idempotency This method is idempotent and deterministic.
352   *
353   * @threadsafety This method is safe for concurrent use by multiple
354   * threads.
355   */
356  public static final <V> Qualifiers<V> of(final Iterable<? extends Qualifier<V>> qualifiers) {
357    if (qualifiers == null) {
358      return of();
359    }
360    final Iterator<? extends Qualifier<V>> i = qualifiers.iterator();
361    if (i.hasNext()) {
362      final Collection<Qualifier<V>> newQualifiers = new TreeSet<>();
363      newQualifiers.add(i.next());
364      while (i.hasNext()) {
365        newQualifiers.add(i.next());
366      }
367      return new Qualifiers<>(newQualifiers);
368    }
369    return of();
370  }
371
372  /**
373   * Returns a {@link Qualifiers}, which may or may not be newly
374   * created, representing the supplied arguments.
375   *
376   * @param qualifier0 the first {@link Qualifier} the {@link
377   * Qualifiers} will contain; must not be {@code null}
378   *
379   * @param qualifier1 the second {@link Qualifier} the {@link
380   * Qualifiers} will contain; must not be {@code null}
381   *
382   * @return a {@link Qualifiers}
383   *
384   * @exception NullPointerException if {@code qualifier} is {@code
385   * null}
386   *
387   * @nullability This method never returns {@code null}.
388   *
389   * @idempotency This method is idempotent and deterministic.
390   *
391   * @threadsafety This method is safe for concurrent use by multiple
392   * threads.
393   */
394  public static final Qualifiers<?> ofDisparate(final Qualifier<?> qualifier0, final Qualifier<?> qualifier1) {
395    return ofDisparate(List.of(qualifier0, qualifier1));
396  }
397
398
399  /**
400   * Returns a {@link Qualifiers}, which may or may not be newly
401   * created, representing the supplied arguments.
402   *
403   * @param qualifiers an {@link Iterable} representing {@link
404   * Qualifier} instances the {@link Qualifiers} will contain; may be
405   * {@code null}
406   *
407   * @return a {@link Qualifiers}
408   *
409   * @nullability This method never returns {@code null}.
410   *
411   * @idempotency This method is idempotent and deterministic.
412   *
413   * @threadsafety This method is safe for concurrent use by multiple
414   * threads.
415   */
416  @SuppressWarnings("unchecked")
417  public static final Qualifiers<?> ofDisparate(final Iterable<? extends Qualifier<?>> qualifiers) {
418    if (qualifiers == null) {
419      return of();
420    }
421    final Iterator<? extends Qualifier<?>> i = qualifiers.iterator();
422    if (i.hasNext()) {
423      final Collection<Qualifier<Object>> newQualifiers = new TreeSet<>();
424      newQualifiers.add((Qualifier<Object>)i.next());
425      while (i.hasNext()) {
426        newQualifiers.add((Qualifier<Object>)i.next());
427      }
428      return new Qualifiers<>(newQualifiers);
429    }
430    return of();
431  }
432
433}