001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2022–2023 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.ClassDesc;
020import java.lang.constant.Constable;
021import java.lang.constant.ConstantDesc;
022import java.lang.constant.DynamicConstantDesc;
023import java.lang.constant.MethodHandleDesc;
024
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.Objects;
028import java.util.Optional;
029import java.util.Set;
030import java.util.SortedSet;
031import java.util.Spliterator;
032import java.util.TreeSet;
033
034import java.util.stream.Stream;
035
036import org.microbean.constant.Constables;
037
038import static java.lang.constant.ConstantDescs.BSM_INVOKE;
039
040import static java.util.Collections.emptySortedSet;
041import static java.util.Collections.unmodifiableSortedSet;
042
043import static org.microbean.constant.ConstantDescs.CD_Iterable;
044
045/**
046 * An abstract, immutable {@linkplain Iterable iterable} collection of
047 * {@link Binding} instances.
048 *
049 * @param <V> the type of a {@link Binding}'s {@linkplain
050 * Binding#attributes() attribute values}
051 *
052 * @param <B> The concrete subtype of this class
053 *
054 * @author <a href="https://about.me/lairdnelson"
055 * target="_parent">Laird Nelson</a>
056 */
057public abstract class Bindings<V, B extends Binding<V, B>> implements Constable, Iterable<B> {
058
059
060  /*
061   * Instance fields.
062   */
063
064
065  private final SortedSet<B> bindings;
066
067
068  /*
069   * Constructors.
070   */
071
072
073  /**
074   * Creates a new {@link Bindings}.
075   *
076   * <p>Duplicate {@link Binding} instances contained by the supplied
077   * {@link Iterable} will be discarded.</p>
078   *
079   * @param bindings the {@link Binding} instances that will be
080   * contained by this {@link Bindings}
081   */
082  protected Bindings(final Iterable<? extends B> bindings) {
083    super();
084    if (bindings == null) {
085      this.bindings = emptySortedSet();
086    } else {
087      final Iterator<? extends B> i = bindings.iterator();
088      if (i.hasNext()) {
089        final SortedSet<B> newBindings = new TreeSet<>();
090        newBindings.add(i.next());
091        while (i.hasNext()) {
092          newBindings.add(i.next());
093        }
094        this.bindings = unmodifiableSortedSet(newBindings);
095      } else {
096        this.bindings = emptySortedSet();
097      }
098    }
099  }
100
101
102  /*
103   * Instance methods.
104   */
105
106
107  /**
108   * Returns {@code true} if this {@link Bindings} is logically empty.
109   *
110   * @return {@code true} if this {@link Bindings} is logically empty
111   *
112   * @idempotency This method is idempotent and deterministic.
113   *
114   * @threadsafety This method is safe for concurrent use by multiple
115   * threads.
116   *
117   * @see #size()
118   */
119  public final boolean isEmpty() {
120    return this.bindings.isEmpty();
121  }
122
123  /**
124   * Returns {@code 0} or a positive integer describing the number of
125   * entries contained by this {@link Bindings}.
126   *
127   * @return the size of this {@link Bindings}
128   *
129   * @idempotency This method is idempotent and deterministic.
130   *
131   * @threadsafety This method is safe for concurrent use by multiple
132   * threads.
133   */
134  public final int size() {
135    return this.bindings.size();
136  }
137
138  /**
139   * Returns the sole {@link Binding} {@linkplain Binding#value()
140   * value} whose {@linkplain Binding#name() name} {@linkplain
141   * String#equals(Object) is equal to} the supplied {@code name}, or
142   * {@code null} if either there is no such {@link Binding} or there
143   * are several {@link Binding}s with the supplied {@code name}.
144   *
145   * @param name the name; may be {@code null} in which case {@code
146   * false} will be returned
147   *
148   * @return the sole {@link Binding} {@linkplain Binding#value()
149   * value} whose {@linkplain Binding#name() name} {@linkplain
150   * String#equals(Object) is equal to} the supplied {@code name}, or
151   * {@code null} if either there is no such {@link Binding} or there
152   * are several {@link Binding}s with the supplied {@code name}.
153   *
154   * @nullability This method may return {@code null}.
155   *
156   * @idempotency This method is idempotent and deterministic.
157   *
158   * @threadsafety This method is safe for concurrent use by multiple
159   * threads.
160   *
161   * @see #unique(String)
162   */
163  public final V uniqueValue(final String name) {
164    final B b = this.unique(name);
165    return b == null ? null : b.value();
166  }
167
168  /**
169   * Returns the sole {@link Binding} instance whose {@linkplain
170   * Binding#name() name} {@linkplain String#equals(Object) is equal
171   * to} the supplied {@code name}, or {@code null} if either there is
172   * no such {@link Binding} or there are several {@link Binding}s
173   * with the supplied {@code name}.
174   *
175   * @param name the name; may be {@code null} in which case {@code
176   * false} will be returned
177   *
178   * @return the sole {@link Binding} instance whose {@linkplain
179   * Binding#name() name} {@linkplain String#equals(Object) is equal
180   * to} the supplied {@code name}, or {@code null} if either there is
181   * no such {@link Binding} or there are several {@link Binding}s
182   * with the supplied {@code name}.
183   *
184   * @nullability This method may return {@code null}.
185   *
186   * @idempotency This method is idempotent and deterministic.
187   *
188   * @threadsafety This method is safe for concurrent use by multiple
189   * threads.
190   */
191  public final B unique(final String name) {
192    B returnValue = null;
193    if (name != null) {
194      for (final B b : this) {
195        if (b.name().equals(name)) {
196          if (returnValue == null) {
197            returnValue = b;
198          } else {
199            return null;
200          }
201        }
202      }
203    }
204    return returnValue;
205  }
206
207  /**
208   * Returns {@code true} if and only if the supplied {@link Object}
209   * is contained by this {@link Bindings}.
210   *
211   * <p>If the {@link Object} is a {@link Binding}, the containment
212   * check is performed via an equality check.  If the {@link Object}
213   * is a {@link String}, then this method will return {@code true} if
214   * there is a {@link Binding} contained by this {@link Bindings}
215   * whose {@linkplain Binding#name() name} {@linkplain
216   * String#equals(Object) is equal to} the supplied {@link
217   * String}.</p>
218   *
219   * <p>There may be many {@link Binding} instances contained by this
220   * {@link Bindings} whose {@linkplain Binding#name() names} are
221   * {@linkplain String#equals(Object) equal}.</p>
222   *
223   * @param o the {@link Object} to test; {@code true} return values
224   * are possible only when this {@link Object} is either a {@link
225   * Binding} or a {@link String}
226   *
227   * @return {@code true} if and only if the supplied {@link Object}
228   * is contained by this {@link Bindings}
229   *
230   * @idempotency This method is idempotent and deterministic.
231   *
232   * @threadsafety This method is safe for concurrent use by multiple
233   * threads.
234   *
235   * @see #containsUnique(String)
236   */
237  public final boolean contains(final Object o) {
238    if (o == null || o instanceof Binding<?, ?>) {
239      return this.bindings.contains(o);
240    }
241    for (final B b : this) {
242      if (b.name().equals(o)) {
243        return true;
244      }
245    }
246    return false;
247  }
248
249  /**
250   * Returns {@code true} if and only if there is exactly one {@link
251   * Binding} contained by this {@link Bindings} whose {@linkplain
252   * Binding#name() name} {@linkplain String#equals(Object) is equal
253   * to} the supplied {@code name}.
254   *
255   * @param name the name to test; may be {@code null} in which case
256   * {@code false} will be returned
257   *
258   * @return {@code true} if and only if there is exactly one {@link
259   * Binding} contained by this {@link Bindings} whose {@linkplain
260   * Binding#name() name} {@linkplain String#equals(Object) is equal
261   * to} the supplied {@code name}
262   *
263   * @idempotency This method is idempotent and deterministic.
264   *
265   * @threadsafety This method is safe for concurrent use by multiple
266   * threads.
267   */
268  public final boolean containsUnique(final String name) {
269    return this.unique(name) != null;
270  }
271
272  /**
273   * Returns a {@link Stream} of this {@link Bindings}' {@linkplain
274   * Binding entries}.
275   *
276   * @return a {@link Stream} of this {@link Bindings}' {@linkplain
277   * Binding entries}
278   *
279   * @nullability This method never returns {@code null}.
280   *
281   * @idempotency This method is idempotent and deterministic.
282   *
283   * @threadsafety This method is safe for concurrent use by multiple
284   * threads.
285   */
286  public final Stream<B> stream() {
287    return this.bindings.stream();
288  }
289
290  /**
291   * Returns a non-{@code null}, immutable {@link Iterator} of {@link
292   * Binding} instances contained by this {@link Bindings}.
293   *
294   * @return a non-{@code null}, immutable {@link Iterator} of {@link
295   * Binding} instances contained by this {@link Bindings}
296   *
297   * @nullability This method never returns {@code null}.
298   *
299   * @idempotency This method is idempotent and deterministic.
300   *
301   * @threadsafety This method is safe for concurrent use by multiple
302   * threads.
303   */
304  @Override // Iterable<B>
305  public final Iterator<B> iterator() {
306    return this.bindings.iterator();
307  }
308
309  /**
310   * Returns a non-{@code null}, immutable {@link Spliterator} of
311   * {@link Binding} instances contained by this {@link Bindings}.
312   *
313   * @return a non-{@code null}, immutable {@link Spliterator} of
314   * {@link Binding} instances contained by this {@link Bindings}
315   *
316   * @nullability This method never returns {@code null}.
317   *
318   * @idempotency This method is idempotent and deterministic.
319   *
320   * @threadsafety This method is safe for concurrent use by multiple
321   * threads.
322   */
323  @Override // Iterable<B>
324  public final Spliterator<B> spliterator() {
325    return this.bindings.spliterator();
326  }
327
328  /**
329   * Returns the number of entries this {@link Bindings} has in common
330   * with the supplied {@link Iterable}.
331   *
332   * <p>The number returned will be {@code 0} or greater.</p>
333   *
334   * @param other the {@link Iterable}; may be {@code null} in which
335   * case {@code 0} will be returned
336   *
337   * @return the number of entries this {@link Bindings} has in common
338   * with the supplied {@link Iterable}
339   *
340   * @idempotency This method is idempotent and deterministic.
341   *
342   * @threadsafety This method is safe for concurrent use by multiple
343   * threads.
344   */
345  public final int intersectionSize(final Iterable<?> other) {
346    if (other == null) {
347      return 0;
348    } else if (this == other) {
349      return this.size();
350    } else {
351      final Iterator<?> i = other.iterator();
352      if (i.hasNext()) {
353        int count = this.bindings.contains(i.next()) ? 1 : 0;
354        while (i.hasNext()) {
355          if (this.bindings.contains(i.next())) {
356            ++count;
357          }
358        }
359        return count;
360      } else {
361        return 0;
362      }
363    }
364  }
365
366  /**
367   * Returns the size of the <em>symmetric difference</em> between
368   * this {@link Bindings} and the supplied {@link Iterable}.
369   *
370   * <p>The number returned is always {@code 0} or greater.</p>
371   *
372   * <p>The size of the symmetric difference between this {@link
373   * Bindings} instance and the supplied {@link Iterable} is the
374   * number of entries that are in one of these two objects but not in
375   * the other.</p>
376   *
377   * @param other an {@link Iterable}; may be {@code null} in which
378   * case the result of an invocation of this {@link Bindings}'
379   * {@link #size()} method will be returned
380   *
381   * @return the size of the <em>symmetric difference</em> between
382   * this {@link Bindings} and the supplied {@link Iterable}; always
383   * {@code 0} or greater
384   *
385   * @idempotency This method is idempotent and deterministic.
386   *
387   * @threadsafety This method is safe for concurrent use by multiple
388   * threads.
389   */
390  public final int symmetricDifferenceSize(final Iterable<?> other) {
391    if (other == null) {
392      return this.size();
393    } else if (this == other) {
394      return 0;
395    } else {
396      final Iterator<?> i = other.iterator();
397      if (i.hasNext()) {
398        final Set<? super Object> symmetricDifference = new HashSet<>(this.bindings);
399        Object b = i.next();
400        if (!symmetricDifference.add(b)) {
401          symmetricDifference.remove(b);
402        }
403        while (i.hasNext()) {
404          b = i.next();
405          if (!symmetricDifference.add(b)) {
406            symmetricDifference.remove(b);
407          }
408        }
409        return symmetricDifference.size();
410      } else {
411        return this.size();
412      }
413    }
414  }
415
416  /**
417   * Returns an {@link Optional} housing a {@link ConstantDesc}
418   * describing this {@link Bindings}, if this {@link Bindings} is
419   * capable of being represented as a <a
420   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
421   * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link
422   * Optional} if not.
423   *
424   * @return an {@link Optional} housing a {@link ConstantDesc}
425   * describing this {@link Binding}, if this {@link Bindings} is
426   * capable of being represented as a <a
427   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
428   * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link
429   * Optional} if not
430   *
431   * @nullability This method never returns {@code null}.
432   *
433   * @idempotency This method is idempotent and deterministic.
434   *
435   * @threadsafety This method is safe for concurrent use by multiple
436   * threads.
437   *
438   * @see #describeConstructor()
439   *
440   * @see <a
441   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">Dynamically-computed
442   * constants</a>
443   */
444  @Override // Constable
445  public final Optional<? extends ConstantDesc> describeConstable() {
446    final MethodHandleDesc constructor = this.describeConstructor();
447    if (constructor == null) {
448      return Optional.empty();
449    }
450    final ConstantDesc bindingsCd = Constables.describeConstable(this.bindings).orElse(null);
451    if (bindingsCd == null) {
452      return Optional.empty();
453    }
454    return
455      Optional.of(DynamicConstantDesc.of(BSM_INVOKE, constructor, bindingsCd));
456  }
457
458  /**
459   * Returns a {@link MethodHandleDesc} describing the constructor or
460   * {@code static} method that will be used to create a <a
461   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
462   * constant</a> representing this {@link Bindings}.
463   *
464   * @return a {@link MethodHandleDesc} describing the constructor or
465   * {@code static} method that will be used to create a <a
466   * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic
467   * constant</a> representing this {@link Bindings}
468   *
469   * @nullability This method does not, and its overrides must not,
470   * return {@code null}.
471   *
472   * @idempotency This method is, and its overrides must be,
473   * idempotent and deterministic.
474   *
475   * @threadsafety This method is, and its overrides must be, safe for
476   * concurrent use by multiple threads.
477   */
478  protected MethodHandleDesc describeConstructor() {
479    return
480      MethodHandleDesc.ofConstructor(this.getClass().describeConstable().orElseThrow(),
481                                     new ClassDesc[] { CD_Iterable });
482  }
483
484  /**
485   * Returns a hashcode for this {@link Bindings}.
486   *
487   * @return a hashcode for this {@link Bindings}
488   *
489   * @idempotency This method is, and its overrides must be,
490   * idempotent and deterministic.
491   *
492   * @threadsafety This method is, and its overrides must be, safe for
493   * concurrent use by multiple threads.
494   *
495   * @see #equals(Object)
496   */
497  @Override // Object
498  public final int hashCode() {
499    return this.bindings.hashCode();
500  }
501
502  /**
503   * Returns {@code true} if and only if this {@link Bindings} is
504   * equal to the supplied {@link Object}.
505   *
506   * <p>The supplied {@link Object} is considered to be equal to this
507   * {@link Bindings} if and only if:</p>
508   *
509   * <ul>
510   *
511   * <li>Its {@linkplain #getClass() class} is identical to this
512   * {@link Bindings}' {@linkplain #getClass() class}, and</li>
513   *
514   * <li>Each of the {@link Binding} instances it contains is
515   * {@linkplain Binding#equals(Object) equal to} the corresponding
516   * {@link Binding} instance contained by this {@link Bindings}</li>
517   *
518   * </ul>
519   *
520   * @param other the {@link Object} to test; may be {@code null} in
521   * which case {@code false} will be returned
522   *
523   * @return {@code true} if the supplied {@link Object} is equal to
524   * this {@link Binding}
525   *
526   * @idempotency This method is, and its overrides must be,
527   * idempotent and deterministic.
528   *
529   * @threadsafety This method is, and its overrides must be, safe for
530   * concurrent use by multiple threads.
531   *
532   * @see #hashCode()
533   *
534   * @see Binding#equals(Object)
535   */
536  @Override // Object
537  public final boolean equals(final Object other) {
538    if (other == this) {
539      return true;
540    } else if (other != null && other.getClass() == this.getClass()) {
541      final Bindings<?, ?> her = (Bindings<?, ?>)other;
542      return
543        Objects.equals(this.bindings, her.bindings);
544    } else {
545      return false;
546    }
547  }
548
549  /**
550   * Returns a {@link String} representation of this {@link Bindings}.
551   *
552   * <p>The format of the returned {@link String} is deliberately
553   * undefined and may change between versions of this class without
554   * prior notice.</p>
555   *
556   * @return a {@link String} representation of this {@link Bindings}
557   *
558   * @nullability This method does not, and its overrides must not,
559   * return {@code null}.
560   *
561   * @idempotency This method is, and its overrides must be,
562   * idempotent and deterministic.
563   *
564   * @threadsafety This method is, and its overrides must be, safe for
565   * concurrent use by multiple threads.
566   */
567  @Override // Object
568  public String toString() {
569    return this.bindings.toString();
570  }
571
572}