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}