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.loader;
018
019import java.lang.reflect.ParameterizedType;
020import java.lang.reflect.Type;
021
022import java.util.ArrayDeque;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.Deque;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.NoSuchElementException;
031import java.util.Objects;
032import java.util.Queue;
033import java.util.ServiceLoader;
034
035import java.util.concurrent.ConcurrentHashMap;
036import java.util.concurrent.ConcurrentMap;
037
038import java.util.function.BiPredicate;
039import java.util.function.Supplier;
040
041import org.microbean.development.annotation.Experimental;
042
043import org.microbean.invoke.Absence;
044import org.microbean.invoke.FixedValueSupplier;
045import org.microbean.invoke.OptionalSupplier;
046
047import org.microbean.loader.api.Loader;
048
049import org.microbean.path.Path;
050import org.microbean.path.Path.Element;
051
052import org.microbean.qualifier.Qualified;
053import org.microbean.qualifier.Qualifiers;
054
055import org.microbean.loader.spi.AmbiguityHandler;
056import org.microbean.loader.spi.Provider;
057import org.microbean.loader.spi.ServiceProviderInstantiator;
058import org.microbean.loader.spi.Value;
059
060import org.microbean.type.JavaType.Token;
061import org.microbean.type.JavaTypes;
062
063/**
064 * A subclassable default {@link Loader} implementation that delegates
065 * its work to {@link Provider}s and an {@link #ambiguityHandler()
066 * AmbiguityHandler}.
067 *
068 * @param <T> the type of configured objects this {@link
069 * DefaultLoader} supplies
070 *
071 * @author <a href="https://about.me/lairdnelson"
072 * target="_parent">Laird Nelson</a>
073 *
074 * @see Loader
075 *
076 * @see Provider
077 */
078public class DefaultLoader<T> implements AutoCloseable, Loader<T> {
079
080
081  /*
082   * Static fields.
083   */
084
085
086  private static final ThreadLocal<Map<Path<? extends Type>, Deque<Provider>>> currentProviderStacks =
087    ThreadLocal.withInitial(() -> new HashMap<>(7));
088
089
090  /*
091   * Instance fields.
092   */
093
094
095  private final ConcurrentMap<Path<? extends Type>, DefaultLoader<?>> loaderCache;
096
097  private final Path<? extends Type> requestedPath;
098
099  private final Path<? extends Type> absolutePath;
100
101  private final Loader<?> parent;
102
103  private final OptionalSupplier<? extends T> supplier;
104
105  private final Collection<Provider> providers;
106
107  private final AmbiguityHandler ambiguityHandler;
108
109
110  /*
111   * Constructors.
112   */
113
114
115  /**
116   * Creates a new {@link DefaultLoader}.
117   *
118   * <p>The new {@link DefaultLoader} will return {@code this} from
119   * its {@link #parent()} method.</p>
120   *
121   * @see org.microbean.loader.api.Loader#loader()
122   */
123  public DefaultLoader() {
124    this(new ConcurrentHashMap<Path<? extends Type>, DefaultLoader<?>>(),
125         null, // providers
126         null, // parent,
127         null, // requestedPath
128         null, // Supplier
129         null); // AmbiguityHandler
130  }
131
132  /**
133   * Creates a new {@link DefaultLoader} that will use the supplied
134   * {@link Provider}s and an {@link AmbiguityHandler} disovered via
135   * the {@link ServiceLoader} mechanism.
136   *
137   * <p>The new {@link DefaultLoader} will return {@code this} from
138   * its {@link #parent()} method.</p>
139   *
140   * @param providers the {@link Provider}s to use; may be {@code
141   * null} in which case {@link Provider}s will be discovered using
142   * the {@link ServiceLoader} mechanism; may be empty in which case
143   * the new {@link DefaultLoader} may be useless
144   */
145  public DefaultLoader(final Collection<? extends Provider> providers) {
146    this(new ConcurrentHashMap<Path<? extends Type>, DefaultLoader<?>>(),
147         providers,
148         null, // parent,
149         null, // requestedPath
150         null, // Supplier
151         null); // AmbiguityHandler
152  }
153
154  /**
155   * Creates a new {@link DefaultLoader} that will use the supplied
156   * {@link Provider}s and {@link AmbiguityHandler}.
157   *
158   * <p>The new {@link DefaultLoader} will return {@code this} from
159   * its {@link #parent()} method.</p>
160   *
161   * @param providers the {@link Provider}s to use; may be {@code
162   * null} in which case {@link Provider}s will be discovered using
163   * the {@link ServiceLoader} mechanism; may be empty in which case
164   * the new {@link DefaultLoader} may be useless
165   *
166   * @param ambiguityHandler the {@link AmbiguityHandler} to use; may
167   * be {@code null} in which case an {@link AmbiguityHandler} will be
168   * discovered using the {@link ServiceLoader} mechanism
169   */
170  public DefaultLoader(final Collection<? extends Provider> providers,
171                       final AmbiguityHandler ambiguityHandler) {
172    this(new ConcurrentHashMap<Path<? extends Type>, DefaultLoader<?>>(),
173         providers,
174         null, // parent,
175         null, // requestedPath
176         null, // Supplier
177         ambiguityHandler);
178  }
179
180  private DefaultLoader(final DefaultLoader<T> loader) {
181    this(loader.loaderCache,
182         loader.providers(),
183         loader.parent() == loader ? null : loader.parent(), // root case
184         loader.path(),
185         loader.parent() == loader ? null : loader.supplier, // root case
186         loader.ambiguityHandler());
187  }
188
189  private DefaultLoader(final DefaultLoader<T> loader, final AmbiguityHandler ambiguityHandler) {
190    this(loader.loaderCache,
191         loader.providers(),
192         loader.parent() == loader ? null : loader.parent(), // root case
193         loader.path(),
194         loader.parent() == loader ? null : loader.supplier, // root case
195         ambiguityHandler);
196  }
197
198  private DefaultLoader(final DefaultLoader<T> loader, final Collection<? extends Provider> providers) {
199    this(loader.loaderCache,
200         providers,
201         loader.parent() == loader ? null : loader.parent(), // root case
202         loader.path(),
203         loader.parent() == loader ? null : loader.supplier, // root case
204         loader.ambiguityHandler());
205  }
206
207  @SuppressWarnings("unchecked")
208  private DefaultLoader(final ConcurrentMap<Path<? extends Type>, DefaultLoader<?>> loaderCache,
209                        final Collection<? extends Provider> providers,
210                        final Loader<?> parent, // if null, will end up being "this" if absolutePath is null or Path.root()
211                        final Path<? extends Type> requestedPath,
212                        final OptionalSupplier<? extends T> supplier, // if null, will end up being () -> this if absolutePath is null or Path.root()
213                        final AmbiguityHandler ambiguityHandler) {
214    super();
215    this.loaderCache = Objects.requireNonNull(loaderCache, "loaderCache");
216    if (parent == null) {
217      // Root case. Pay attention.
218      if (requestedPath == null || requestedPath.isRoot()) {
219        final Path<? extends Type> rootPath = (Path<? extends Type>)Path.root();
220        this.requestedPath = rootPath;
221        this.parent = this; // NOTE
222        this.absolutePath = rootPath;
223        this.supplier = supplier == null ? FixedValueSupplier.of((T)this) : supplier;
224        this.providers = providers == null ? loadedProviders() : List.copyOf(providers);
225        this.loaderCache.put(rootPath, this); // NOTE
226        if (ambiguityHandler == null) {
227          // While the following call is in effect, our
228          // final-but-as-yet-uninitialized ambiguityHandler field will
229          // be null.  Note that the ambiguityHandler() instance method
230          // accounts for this.
231          try {
232            this.ambiguityHandler = this.load(AmbiguityHandler.class).orElseGet(DefaultLoader::loadedAmbiguityHandler);
233          } finally {
234            this.loaderCache.remove(rootPath);
235          }
236        } else {
237          this.ambiguityHandler = ambiguityHandler;
238        }
239      } else {
240        throw new IllegalArgumentException("!requestedPath.equals(Path.root()): " + requestedPath);
241      }
242    } else if (!parent.absolutePath().absolute()) {
243      throw new IllegalArgumentException("!parent.absolutePath().absolute(): " + parent.absolutePath());
244    } else if (requestedPath.isRoot()) {
245      throw new IllegalArgumentException("requestedPath.isRoot()");
246    } else {
247      assert requestedPath.transliterated();
248      this.requestedPath = requestedPath;
249      this.parent = parent;
250      assert parent.absolutePath().transliterated();
251      this.supplier = Objects.requireNonNull(supplier, "supplier");
252      this.providers = List.copyOf(providers);
253      this.ambiguityHandler = Objects.requireNonNull(ambiguityHandler, "ambiguityHandler");
254      if (requestedPath.absolute()) {
255        this.absolutePath = requestedPath;
256      } else {
257        this.absolutePath = this.transliterate(parent.absolutePath().plus(requestedPath));
258      }
259      assert this.absolutePath.transliterated();
260    }
261  }
262
263
264  /*
265   * Instance methods.
266   */
267
268
269  /**
270   * Clears any caches used by this {@link DefaultLoader}.
271   *
272   * <p>This {@link DefaultLoader} remains valid to use.</p>
273   *
274   * @threadsafety This method is safe for concurrent use by multiple
275   * threads.
276   *
277   * @idempotency This method is deterministic but not idempotent
278   * unless the caches are already cleared.
279   */
280  @Experimental
281  @Override // AutoCloseable
282  public final void close() {
283    this.loaderCache.clear();
284  }
285
286  /**
287   * Returns a {@link DefaultLoader} that {@linkplain #providers()
288   * uses} the additional {@link Provider}.
289   *
290   * @param provider the additional {@link Provider}; may be {@code
291   * null} in which case {@code this} will be returned
292   *
293   * @return a {@link DefaultLoader} that {@linkplain #providers()
294   * uses} the additional {@link Provider}
295   *
296   * @nullability This method never returns {@code null}.
297   *
298   * @idempotency This method is not idempotent (it usually creates a
299   * new {@link DefaultLoader} to return) but is deterministic.
300   *
301   * @threadsafety This method is safe for concurrent use by multiple
302   * threads.
303   */
304  public final DefaultLoader<T> plus(final Provider provider) {
305    return provider == null ? this : new DefaultLoader<>(this, add(this.providers, provider));
306  }
307
308  /**
309   * Returns a {@link DefaultLoader} that {@linkplain #providers()
310   * uses} the additional {@link Provider}s.
311   *
312   * @param providers the additional {@link Provider}s; may be {@code
313   * null} or {@linkplain Collection#isEmpty() empty} in which case
314   * {@code this} will be returned
315   *
316   * @return a {@link DefaultLoader} that {@linkplain #providers()
317   * uses} the additional {@link Provider}s
318   *
319   * @nullability This method never returns {@code null}.
320   *
321   * @idempotency This method is not idempotent (it usually creates a
322   * new {@link DefaultLoader} to return) but is deterministic.
323   *
324   * @threadsafety This method is safe for concurrent use by multiple
325   * threads.
326   */
327  public final DefaultLoader<T> plus(final Collection<? extends Provider> providers) {
328    return providers == null || providers.isEmpty() ? this : new DefaultLoader<>(this, add(this.providers, providers));
329  }
330
331  /**
332   * Returns a {@link DefaultLoader} that {@linkplain #providers()
333   * uses} only the supplied {@link Provider}.
334   *
335   * @param provider the {@link Provider}; may be {@code null} in
336   * which case {@link Provider}s will be loaded using the {@link
337   * ServiceLoader} class
338   *
339   * @return a {@link DefaultLoader} that {@linkplain #providers()
340   * uses} only the supplied {@link Provider}
341   *
342   * @nullability This method never returns {@code null}.
343   *
344   * @idempotency This method is not idempotent (it usually creates a
345   * new {@link DefaultLoader} to return) but is deterministic.
346   *
347   * @threadsafety This method is safe for concurrent use by multiple
348   * threads.
349   */
350  public final DefaultLoader<T> with(final Provider provider) {
351    return new DefaultLoader<>(this, provider == null ? List.of() : List.of(provider));
352  }
353
354  /**
355   * Returns a {@link DefaultLoader} that {@linkplain #providers()
356   * uses} only the supplied {@link Provider}s.
357   *
358   * @param providers the {@link Provider}s; may be {@code null} or
359   * {@linkplain Collection#isEmpty() empty} in which case {@link
360   * Provider}s will be loaded using the {@link ServiceLoader} class
361   *
362   * @return a {@link DefaultLoader} that {@linkplain #providers()
363   * uses} only the supplied {@link Provider}s
364   *
365   * @nullability This method never returns {@code null}.
366   *
367   * @idempotency This method is not idempotent (it usually creates a
368   * new {@link DefaultLoader} to return) but is deterministic.
369   *
370   * @threadsafety This method is safe for concurrent use by multiple
371   * threads.
372   */
373  public final DefaultLoader<T> with(final Collection<? extends Provider> providers) {
374    return new DefaultLoader<>(this, providers);
375  }
376
377  /**
378   * Returns a {@link DefaultLoader} that {@linkplain
379   * #ambiguityHandler() uses the supplied
380   * <code>AmbiguityHandler</code>}.
381   *
382   * @param ambiguityHandler the {@link AmbiguityHandler}; must not be
383   * {@code null}
384   *
385   * @return a {@link DefaultLoader} that {@linkplain
386   * #ambiguityHandler() uses the supplied
387   * <code>AmbiguityHandler</code>}
388   *
389   * @exception NullPointerException if {@code ambiguityHandler} is
390   * {@code null}
391   *
392   * @nullability This method never returns {@code null}.
393   *
394   * @idempotency This method is not idempotent (it usually creates a
395   * new {@link DefaultLoader} to return) but is deterministic.
396   *
397   * @threadsafety This method is safe for concurrent use by multiple
398   * threads.
399   */
400  public final DefaultLoader<T> with(final AmbiguityHandler ambiguityHandler) {
401    if (ambiguityHandler == this.ambiguityHandler()) {
402      return this;
403    } else if (ambiguityHandler == null) {
404      return new DefaultLoader<>(this, NoOpAmbiguityHandler.INSTANCE);
405    } else {
406      return new DefaultLoader<>(this, ambiguityHandler);
407    }
408  }
409
410  /**
411   * Returns an {@linkplain
412   * java.util.Collections#unmodifiableCollection(Collection)
413   * unmodifiable} {@link Collection} of {@link Provider}s that this
414   * {@link DefaultLoader} will use to supply objects.
415   *
416   * <p>This method never returns {@code null}.</p>
417   *
418   * @return an {@linkplain
419   * java.util.Collections#unmodifiableCollection(Collection)
420   * unmodifiable} {@link Collection} of {@link Provider}s that this
421   * {@link DefaultLoader} will use to supply objects; never {@code null}
422   *
423   * @nullability This method never returns {@code null}.
424   *
425   * @threadsafety This method is safe for concurrent use by multiple
426   * threads.
427   *
428   * @idempotency This method is idempotent and deterministic.
429   */
430  public final Collection<Provider> providers() {
431    return this.providers;
432  }
433
434  /**
435   * Returns the {@link AmbiguityHandler} associated with this {@link
436   * DefaultLoader}.
437   *
438   * <p>This method never returns {@code null}.</p>
439   *
440   * @return the {@link AmbiguityHandler} associated with this {@link
441   * DefaultLoader}; never {@code null}
442   *
443   * @nullability This method never returns {@code null}
444   *
445   * @threadsafety This method is safe for concurrent use by multiple
446   * threads.
447   *
448   * @idempotency This method is idempotent and deterministic.
449   *
450   * @see AmbiguityHandler
451   */
452  public final AmbiguityHandler ambiguityHandler() {
453    // NOTE: This null check is critical.  We check for null here
454    // because during bootstrapping the AmbiguityHandler will not have
455    // been loaded yet, and yet the bootstrapping mechanism may still
456    // end up calling this.ambiguityHandler().  The alternative would
457    // be to make the ambiguityHandler field non-final and I don't
458    // want to do that.
459    final AmbiguityHandler ambiguityHandler = this.ambiguityHandler;
460    return ambiguityHandler == null ? NoOpAmbiguityHandler.INSTANCE : ambiguityHandler;
461  }
462
463  /**
464   * Returns the {@link Loader} serving as the parent of this {@link
465   * DefaultLoader}.
466   *
467   * <p>The "root" {@link DefaultLoader} returns itself from its
468   * {@link #parent()} implementation.</p>
469   *
470   * <p>This method never returns {@code null}.</p>
471   *
472   * @return the non-{@code null} {@link Loader} serving as the parent
473   * of this {@link DefaultLoader}; may be this {@link DefaultLoader}
474   * itself
475   *
476   * @nullability This method never returns {@code null}.
477   *
478   * @threadsafety This method is safe for concurrent use by multiple
479   * threads.
480   *
481   * @idempotency This method is idempotent and deterministic.
482   */
483  // Note that the root will have itself as its parent.
484  @Override // Loader<T>
485  public final Loader<?> parent() {
486    return this.parent;
487  }
488
489  /**
490   * Returns the {@link Path} with which this {@link DefaultLoader}
491   * was created.
492   *
493   * <p>The {@link Path} that is returned by this method is not
494   * guaranteed to be {@linkplain Path#absolute() absolute}.</p>
495   *
496   * <p>The {@link Path} that is returned by this method will be
497   * {@linkplain Path#equals(Object) equal} to a {@linkplain
498   * #transliterate(Path) transliterated} version of the {@link Path}
499   * that was supplied to the {@link #load(Path)} method of this
500   * {@link DefaultLoader}'s {@linkplain #parent() parent} that
501   * resulted in this {@link DefaultLoader}'s creation.</p>
502   *
503   * @return the non-{@code null} {@link Path} with which this {@link
504   * DefaultLoader} was created
505   *
506   * @nullability This method never returns {@code null}.
507   *
508   * @threadsafety This method is safe for concurrent use by multiple
509   * threads.
510   *
511   * @idempotency This method is idempotent and deterministic.
512   * @nullability Implementations of this method must not return
513   * {@code null}.
514   *
515   * @see #parent()
516   *
517   * @see #load(Path)
518   *
519   * @see #absolutePath()
520   *
521   * @see #absolutePath(Path)
522   */
523  @Override // Loader<T>
524  public final Path<? extends Type> path() {
525    return this.requestedPath;
526  }
527
528  /**
529   * Returns the {@linkplain Path#absolute() absolute} {@link Path}
530   * representing the {@link Path} with which this {@link
531   * DefaultLoader} was created.
532   *
533   * <p>The {@link Path} that is returned by this method is guaranteed
534   * to be {@linkplain Path#absolute() absolute}.</p>
535   *
536   * <p>The {@link Path} that is returned by this method will be
537   * {@linkplain Path#equals(Object) equal} to a {@linkplain
538   * #transliterate(Path) transliterated} and {@linkplain
539   * Path#absolute() absolute} version of the {@link Path} that was
540   * supplied to the {@link #load(Path)} method of this {@link
541   * DefaultLoader}'s {@linkplain #parent() parent} that resulted in
542   * this {@link DefaultLoader}'s creation.</p>
543   *
544   * @return the non-{@code null} {@linkplain Path#absolute()
545   * absolute} {@link Path} representing the {@link Path} with which
546   * this {@link DefaultLoader} was created
547   *
548   * @nullability This method never returns {@code null}.
549   *
550   * @threadsafety This method is safe for concurrent use by multiple
551   * threads.
552   *
553   * @idempotency This method is idempotent and deterministic.
554   * @nullability Implementations of this method must not return
555   * {@code null}.
556   *
557   * @see #parent()
558   *
559   * @see #load(Path)
560   *
561   * @see #path()
562   *
563   * @see #absolutePath(Path)
564   */
565  @Override // Loader<T>
566  public final Path<? extends Type> absolutePath() {
567    return this.absolutePath;
568  }
569
570  /**
571   * Returns a {@link Determinism} suitable for this {@link
572   * DefaultLoader}.
573   *
574   * @return a {@link Determinism} suitable for this {@link
575   * DefaultLoader}
576   *
577   * @nullability This method never returns {@code null}.
578   *
579   * @idempotency This method is idempotent and deterministic.
580   *
581   * @threadsafety This method is safe for concurrent use by multiple
582   * threads.
583   */
584  public final Determinism determinism() {
585    final OptionalSupplier<?> s = this.supplier;
586    return s == null ? Determinism.NON_DETERMINISTIC : s.determinism();
587  }
588
589  @Override // Loader<T>
590  public final T get() {
591    return this.supplier.get();
592  }
593
594  /**
595   * Returns a {@link DefaultLoader} that can {@linkplain #get()
596   * supply} environmental objects that are suitable for the supplied
597   * {@code path}.
598   *
599   * <p>This method never returns {@code null}.</p>
600   *
601   * <p>The {@link DefaultLoader} that is returned may return {@code
602   * null} from its {@link Loader#get() get()} method.  Additionally,
603   * the {@link DefaultLoader} that is returned may throw {@link
604   * java.util.NoSuchElementException} or {@link
605   * UnsupportedOperationException} from its {@link #get() get()}
606   * method.</p>
607   *
608   * @param <U> the type of the supplied {@link Path} and the type of
609   * the returned {@link DefaultLoader}
610   *
611   * @param path the {@link Path} for which a {@link DefaultLoader}
612   * should be returned; must not be {@code null}
613   *
614   * @return a {@link DefaultLoader} capable of {@linkplain #get()
615   * supplying} environmental objects suitable for the supplied {@code
616   * path}; never {@code null}
617   *
618   * @exception NullPointerException if {@code path} is {@code null}
619   *
620   * @exception IllegalArgumentException if the {@code path}, after
621   * {@linkplain #absolutePath(Path) normalization}, {@linkplain
622   * Path#isRoot() is the root <code>Path</code>}
623   *
624   * @nullability This method never returns {@code null}.
625   *
626   * @threadsafety This method is safe for concurrent use by multiple
627   * threads.
628   *
629   * @threadsafety This method is idempotent and deterministic.
630   */
631  @Override // Loader<T>
632  public final <U> DefaultLoader<U> load(Path<? extends Type> path) {
633    final Path<? extends Type> requestedPath = this.transliterate(path);
634    final Path<? extends Type> absolutePath;
635    if (requestedPath.absolute()) {
636      absolutePath = requestedPath; // already transliterated
637    } else {
638      absolutePath = this.transliterate(this.absolutePath().plus(requestedPath));
639      if (!absolutePath.absolute()) {
640        throw new IllegalArgumentException("!absolutePath.absolute(): " + absolutePath);
641      }
642    }
643    if (absolutePath.isRoot()) {
644      throw new IllegalArgumentException("absolutePath.isRoot(): " + absolutePath);
645    }
646    // We deliberately do not use computeIfAbsent() because load()
647    // operations can kick off other load() operations, and then you'd
648    // have a cache mutating operation occuring within a cache
649    // mutating operation, which is forbidden.  Sometimes you get an
650    // IllegalStateException as you are supposed to; other times you
651    // do not, which is a JDK bug.  See
652    // https://blog.jooq.org/avoid-recursion-in-concurrenthashmap-computeifabsent/.
653    //
654    // This obviously can result in unnecessary work, but most
655    // configuration use cases will cause this work to happen anyway.
656    DefaultLoader<?> defaultLoader = this.loaderCache.get(absolutePath);
657    if (defaultLoader == null) {
658      defaultLoader =
659        this.loaderCache.putIfAbsent(absolutePath,
660                                     this.computeLoader(this.loaderFor(absolutePath), requestedPath, absolutePath));
661      if (defaultLoader == null) {
662        // putIfAbsent() returns the *old* value, which may be null.
663        // Contrast with computeIfAbsent(), which returns the *new*
664        // value.  See comments above for why we do not use
665        // computeIfAbsent().
666        defaultLoader = this.loaderCache.get(absolutePath);
667      }
668    }
669    @SuppressWarnings("unchecked")
670    final DefaultLoader<U> returnValue = (DefaultLoader<U>)defaultLoader;
671    return returnValue;
672  }
673
674  @SuppressWarnings("unchecked")
675  private final <U> DefaultLoader<U> computeLoader(final Loader<?> requestor,
676                                                   final Path<? extends Type> requestedPath,
677                                                   final Path<? extends Type> absolutePath) {
678    assert requestedPath.transliterated();
679    assert absolutePath.absolute();
680    assert absolutePath.transliterated();
681    final Qualifiers<? extends String, ?> qualifiers = absolutePath.qualifiers();
682    final AmbiguityHandler ambiguityHandler;
683    if (requestor instanceof AmbiguityHandler ah) {
684      ambiguityHandler = ah;
685    } else if (requestor instanceof DefaultLoader<?> dl) {
686      ambiguityHandler = dl.ambiguityHandler();
687    } else {
688      ambiguityHandler = this.ambiguityHandler();
689    }
690    Value<U> candidate = null;
691    final Collection<? extends Provider> providers = this.providers();
692    if (!providers.isEmpty()) {
693      final Map<Path<? extends Type>, Deque<Provider>> map = currentProviderStacks.get();
694
695      Provider candidateProvider = null;
696      int candidateQualifiersScore = Integer.MIN_VALUE;
697      int candidatePathScore = Integer.MIN_VALUE;
698
699      for (final Provider provider : providers) {
700
701        if (provider == null) {
702          ambiguityHandler.providerRejected(requestor, absolutePath, provider);
703          continue;
704        }
705
706        if (provider == peek(map, absolutePath)) {
707          // Behave the same as the null case immediately prior, but
708          // there's no need to notify the ambiguityHandler.
709          continue;
710        }
711
712        if (!isSelectable(provider, absolutePath)) {
713          ambiguityHandler.providerRejected(requestor, absolutePath, provider);
714          continue;
715        }
716
717        Value<U> value;
718
719        push(map, absolutePath, provider);
720        try {
721          value = (Value<U>)provider.get(requestor, absolutePath);
722        } finally {
723          pop(map, absolutePath);
724        }
725
726        if (value == null) {
727          ambiguityHandler.providerRejected(requestor, absolutePath, provider);
728          continue;
729        }
730
731        // NOTE: INFINITE LOOP POSSIBILITY; read carefully!
732        while (true) {
733
734          value = value.with(requestor.transliterate(value.path()));
735
736          if (!isSelectable(absolutePath, value.path())) {
737            ambiguityHandler.valueRejected(requestor, absolutePath, provider, value);
738            break;
739          }
740
741          if (candidate == null) {
742            candidate = value;
743            candidateProvider = provider;
744            candidateQualifiersScore = ambiguityHandler.score(qualifiers, candidate.qualifiers());
745            candidatePathScore = ambiguityHandler.score(absolutePath, candidate.path());
746            break;
747          }
748
749          // Let's score Qualifiers first, not paths.  This is an
750          // arbitrary decision, but one grounded in reality: most
751          // often qualifiers are both empty and so this is very
752          // quick, and when they are not empty, in real-world
753          // situations they're normally completely disjoint.  Get all
754          // this out of the way early.
755          //
756          // We score qualifiers in a method devoted to them rather
757          // than just folding the qualifier scoring into the path
758          // scoring method because the scoring systems may produce
759          // wildly different numbers, and if the path- and
760          // qualifier-scoring systems use, for example, size() in
761          // their algorithms, you don't want a huge pile of
762          // qualifiers accidentally affecting a path score.
763          final int valueQualifiersScore = ambiguityHandler.score(qualifiers, value.qualifiers());
764          if (valueQualifiersScore < candidateQualifiersScore) {
765            candidate = new Value<>(candidate, value);
766            break;
767          }
768
769          if (valueQualifiersScore > candidateQualifiersScore) {
770            candidate = new Value<>(value, candidate);
771            candidateProvider = provider;
772            candidateQualifiersScore = valueQualifiersScore;
773            // (No need to update candidatePathScore.)
774            break;
775          }
776
777          // The Qualifiers scores were equal (extremely common).
778          // Let's do paths.
779          final int valuePathScore = ambiguityHandler.score(absolutePath, value.path());
780
781          if (valuePathScore < candidatePathScore) {
782            candidate = new Value<>(candidate, value);
783            break;
784          }
785
786          if (valuePathScore > candidatePathScore) {
787            candidate = new Value<>(value, candidate);
788            candidateProvider = provider;
789            candidateQualifiersScore = valueQualifiersScore;
790            candidatePathScore = valuePathScore;
791            break;
792          }
793
794          // The Path scores were equal.  Give the AmbiguityHandler a
795          // chance to resolve the ambguity.
796          final Value<U> disambiguatedValue =
797            ambiguityHandler.disambiguate(requestor, absolutePath, candidateProvider, candidate, provider, value);
798
799          if (disambiguatedValue == null) {
800            // Couldn't disambiguate.  Drop both values.
801            break;
802          }
803
804          if (disambiguatedValue.equals(candidate)) {
805            // Candidate wins; value is a backup.
806            candidate = new Value<>(disambiguatedValue, value);
807            break;
808          }
809
810          if (disambiguatedValue.equals(value)) {
811            // Value wins; candidate is a backup.
812            candidate = new Value<>(disambiguatedValue, candidate);
813            candidateProvider = provider;
814            candidateQualifiersScore = valueQualifiersScore;
815            candidatePathScore = valuePathScore;
816            break;
817          }
818
819          // The AmbiguityHandler came up with an entirely different
820          // value, so run it back through the while loop.
821          value = disambiguatedValue;
822
823        }
824      }
825    }
826    return
827      new DefaultLoader<>(this.loaderCache,
828                          providers,
829                          requestor, // parent
830                          requestedPath,
831                          candidate == null ? Absence.instance() : candidate,
832                          ambiguityHandler);
833  }
834
835
836  /*
837   * Static methods.
838   */
839
840
841  private static final <T> Collection<T> add(final Collection<? extends T> c, final T e) {
842    if (c == null || c.isEmpty()) {
843      return e == null ? List.of() : List.of(e);
844    } else if (e == null) {
845      return Collections.unmodifiableCollection(c);
846    } else {
847      final Collection<T> newC = new ArrayList<>(c.size() + 1);
848      newC.addAll(c);
849      newC.add(e);
850      return Collections.unmodifiableCollection(newC);
851    }
852  }
853
854  private static final <T> Collection<T> add(final Collection<? extends T> c, final Collection<? extends T> c2) {
855    if (c == null || c.isEmpty()) {
856      if (c2 == null || c2.isEmpty()) {
857        return List.of();
858      } else {
859        return Collections.unmodifiableCollection(c2);
860      }
861    } else if (c2 == null || c2.isEmpty()) {
862      return Collections.unmodifiableCollection(c);
863    } else {
864      final Collection<T> newC = new ArrayList<>(c.size() + c2.size());
865      newC.addAll(c);
866      newC.addAll(c2);
867      return Collections.unmodifiableCollection(newC);
868    }
869  }
870
871  private static final Provider peek(final Map<?, ? extends Deque<? extends Provider>> map,
872                                     final Path<? extends Type> absolutePath) {
873    final Queue<? extends Provider> q = map.get(absolutePath);
874    return q == null ? null : q.peek();
875  }
876
877  private static final void push(final Map<Path<? extends Type>, Deque<Provider>> map,
878                                 final Path<? extends Type> absolutePath,
879                                 final Provider provider) {
880    map.computeIfAbsent(absolutePath, ap -> new ArrayDeque<>(5)).push(provider);
881  }
882
883  private static final Provider pop(final Map<?, ? extends Deque<? extends Provider>> map,
884                                    final Path<? extends Type> absolutePath) {
885    final Deque<? extends Provider> dq = map.get(absolutePath);
886    return dq == null ? null : dq.pop();
887  }
888
889  private static final boolean isSelectable(final Provider provider, final Path<? extends Type> absolutePath) {
890    final Type providerLowerBound = provider.lowerBound();
891    return providerLowerBound == null || JavaTypes.assignable(absolutePath.qualified(), providerLowerBound);
892  }
893
894  static final Collection<Provider> loadedProviders() {
895    return Loaded.providers;
896  }
897
898  private static final AmbiguityHandler loadedAmbiguityHandler() {
899    return Loaded.ambiguityHandler;
900  }
901
902  /**
903   * Returns {@code true} if the supplied {@code valuePath} is
904   * <em>selectable</em> (for further consideration and scoring) with
905   * respect to the supplied {@code absoluteReferencePath}.
906   *
907   * <p>This method calls {@link Path#endsWith(Path, BiPredicate)} on
908   * the supplied {@code absoluteReferencePath} with {@code valuePath}
909   * as its {@link Path}-typed first argument, and a {@link
910   * BiPredicate} that returns {@code true} if and only if all of the
911   * following conditions are true:</p>
912   *
913   * <ul>
914   *
915   * <li>Each {@link Element} has a {@linkplain Element#name()
916   * name} that is either {@linkplain String#isEmpty() empty} or equal
917   * to the other's.</li>
918   *
919   * <li>Either {@link Element} has a {@link Element#type() Type}
920   * that is {@code null}, or the first {@link Element}'s {@link
921   * Element#type() Type} {@link AssignableType#of(Type) is
922   * assignable from} the second's.</li>
923   *
924   * <li>Either {@link Element} has {@code null} {@linkplain
925   * Element#parameters() parameters} or each of the first {@link
926   * Element}'s {@linkplain Element#parameters() parameters}
927   * {@linkplain Class#isAssignableFrom(Class) is assignable from} the
928   * second's corresponding parameter.</li>
929   *
930   * </ul>
931   *
932   * <p>In all other cases this method returns {@code false} or throws
933   * an exception.</p>
934   *
935   * @param absoluteReferencePath the reference path; must not be
936   * {@code null}; must be {@linkplain Path#absolute() absolute}
937   *
938   * @param valuePath the {@link Path} to test; must not be {@code
939   * null}
940   *
941   * @return {@code true} if {@code valuePath} is selectable (for
942   * further consideration and scoring) with respect to {@code
943   * absoluteReferencePath}; {@code false} in all other cases
944   *
945   * @exception NullPointerException if either parameter is {@code
946   * null}
947   *
948   * @exception IllegalArgumentException if {@code
949   * absoluteReferencePath} {@linkplain Path#absolute() is not
950   * absolute}
951   */
952  private static final boolean isSelectable(final Path<? extends Type> absoluteReferencePath, final Path<? extends Type> valuePath) {
953    if (!absoluteReferencePath.absolute()) {
954      throw new IllegalArgumentException("absoluteReferencePath: " + absoluteReferencePath);
955    }
956    final Qualifiers<?, ?> q1 = absoluteReferencePath.qualifiers();
957    // Remember that a Path's Qualifiers include the Qualifiers of
958    // every Path.Element in the Path.
959    if (q1 != null && !q1.isEmpty()) {
960      final Qualifiers<?, ?> q2 = valuePath.qualifiers();
961      if (q2 != null && !q2.isEmpty() && q1.intersectionSize(q2) <= 0) {
962        return false;
963      }
964    }
965    return absoluteReferencePath.endsWith(valuePath, NamesAndTypesAreCompatibleBiPredicate.INSTANCE);
966  }
967
968
969  /*
970   * Inner and nested classes.
971   */
972
973
974  private static final class Loaded {
975
976    private static final ServiceProviderInstantiator instantiator =
977      ServiceLoader.load(ServiceProviderInstantiator.class, ServiceProviderInstantiator.class.getClassLoader())
978      .stream()
979      .findFirst()
980      .map(ServiceLoader.Provider::get)
981      .orElse(new ServiceProviderInstantiator() {});
982    
983    private static final List<Provider> providers =
984      ServiceLoader.load(Provider.class, Provider.class.getClassLoader())
985      .stream()
986      .map(instantiator::instantiate)
987      .filter(Objects::nonNull)
988      .toList();
989
990    private static final AmbiguityHandler ambiguityHandler =
991      ServiceLoader.load(AmbiguityHandler.class, AmbiguityHandler.class.getClassLoader())
992      .stream()
993      .findFirst()
994      .map(instantiator::instantiate)
995      .orElse(NoOpAmbiguityHandler.INSTANCE);
996
997  }
998
999  private static final class NoOpAmbiguityHandler implements AmbiguityHandler {
1000
1001    private static final NoOpAmbiguityHandler INSTANCE = new NoOpAmbiguityHandler();
1002
1003    private NoOpAmbiguityHandler() {
1004      super();
1005    }
1006
1007  }
1008
1009  private static final class NamesAndTypesAreCompatibleBiPredicate implements BiPredicate<Element<?>, Element<?>> {
1010
1011    private static final NamesAndTypesAreCompatibleBiPredicate INSTANCE = new NamesAndTypesAreCompatibleBiPredicate();
1012
1013    private NamesAndTypesAreCompatibleBiPredicate() {
1014      super();
1015    }
1016
1017    @Override // BiPredicate<Element<?>, Element<?>>
1018    public final boolean test(final Element<?> e1, final Element<?> e2) {
1019      final String name1 = e1.name();
1020      if (!name1.isEmpty()) {
1021        final String name2 = e2.name();
1022        if (!name2.isEmpty() && !name1.equals(name2)) {
1023          // Empty names have special significance in that they
1024          // "match" any other name.
1025          return false;
1026        }
1027      }
1028
1029      final Object o1 = e1.qualified();
1030      if (o1 == null) {
1031        return e2.qualified() == null;
1032      } else if (!(o1 instanceof Type) ||
1033                 !(e2.qualified() instanceof Type t2) ||
1034                 !JavaTypes.assignable((Type)o1, t2)) {
1035        return false;
1036      }
1037
1038      return true;
1039    }
1040
1041  }
1042
1043}