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.spi; 018 019import java.lang.reflect.Type; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Objects; 028import java.util.TreeSet; 029 030import java.util.function.BiFunction; 031import java.util.function.Supplier; 032 033import org.microbean.invoke.FixedValueSupplier; 034 035import org.microbean.qualifier.Qualifier; 036import org.microbean.qualifier.Qualifiers; 037 038import org.microbean.loader.api.Loader; 039 040import org.microbean.path.Path; 041import org.microbean.path.Path.Element; 042 043/** 044 * An abstract {@link AbstractProvider} whose implementations are 045 * built around tree structures of various kinds. 046 * 047 * @param <N> the type of a node in the tree 048 * 049 * @author <a href="https://about.me/lairdnelson" 050 * target="_parent">Laird Nelson</a> 051 */ 052public abstract class AbstractTreeBasedProvider<N> extends AbstractProvider { 053 054 055 /* 056 * Constructors. 057 */ 058 059 060 /** 061 * Creates a new {@link AbstractTreeBasedProvider} with no 062 * {@linkplain #lowerBound() lower type bound}. 063 */ 064 protected AbstractTreeBasedProvider() { 065 this(null); 066 } 067 068 /** 069 * Creates a new {@link AbstractTreeBasedProvider} with the supplied 070 * {@linkplain Provider#lowerBound() lower type bound}. 071 * 072 * @param lowerTypeBound the {@linkplain #lowerBound() lower type 073 * bound}; may be {@code null} 074 */ 075 protected AbstractTreeBasedProvider(final Type lowerTypeBound) { 076 super(lowerTypeBound); 077 } 078 079 080 /* 081 * Instance methods. 082 */ 083 084 085 /** 086 * Returns the number of child nodes the supplied {@code node} has, 087 * which may be (and often is) {@code 0}. 088 * 089 * <p>If the supplied {@code null} is {@code null}, {@linkplain 090 * #absent(Object) absent} or a scalar, an override of this method 091 * must return {@code 0}.</p> 092 * 093 * @param node the parent node; may be {@code null} in which case 094 * {@code 0} must be returned 095 * 096 * @return the number of child nodes the supplied {@code node} has, 097 * which may be (and often is) {@code 0} 098 * 099 * @nullability Overrides of this method may return {@code null}. 100 * 101 * @idempotency Overrides of this method must be idempotent and 102 * deterministic. 103 * 104 * @threadsafety Overrides of this method must be safe for 105 * concurrent use by multiple threads. 106 */ 107 // returns 0 if the node is null, missing, scalar 108 protected abstract int size(final N node); 109 110 /** 111 * Returns an {@link Iterator} over the names of the supplied {@code 112 * node}'s child nodes, or an {@linkplain 113 * java.util.Collections#emptyIterator() empty 114 * <code>Iterator</code>} if the supplied {@code node} is 115 * {@linkplain #map(node) not a map node}. 116 * 117 * @param node the parent node; may be {@code null} in which case an 118 * {@linkplain java.util.Collections#emptyIterator() empty 119 * <code>Iterator</code>} must be returned 120 * 121 * @return an {@link Iterator}; never {@code null} 122 * 123 * @nullability Overrides of this method must not return {@code 124 * null}. 125 * 126 * @idempotency Overrides of this method must be idempotent and 127 * deterministic. 128 * 129 * @threadsafety Overrides of this method must be safe for 130 * concurrent use by multiple threads. 131 */ 132 protected abstract Iterator<String> names(final N node); 133 134 /** 135 * Returns the child node of the supplied parent {@code node} 136 * identified by the supplied {@code name}, or {@code null} if 137 * either the child does not exist or {@code node} is not a 138 * {@linkplain #map(Object) map node}. 139 * 140 * @param node the parent node; may be {@code null} in which case 141 * {@code null} must be returned 142 * 143 * @param name the name of the child; must not be {@code null} 144 * 145 * @return the child node of the supplied parent {@code node} 146 * identified by the supplied {@code name}, or {@code null} if 147 * either the child does not exist or {@code node} is not a 148 * {@linkplain #map(Object) map node} 149 * 150 * @exception NullPointerException if {@code name} is {@code null} 151 * 152 * @nullability Overrides of this method may (and often do) return 153 * {@code null}. 154 * 155 * @idempotency Overrides of this method must be idempotent and 156 * deterministic. 157 * 158 * @threadsafety Overrides of this method must be safe for 159 * concurrent use by multiple threads. 160 */ 161 protected abstract N get(final N node, final String name); 162 163 /** 164 * Returns the child node of the supplied parent {@code node} 165 * identified by the supplied {@code index}, or {@code null} if 166 * either the child does not exist or {@code node} is not a 167 * {@linkplain #list(Object) list node}. 168 * 169 * @param node the parent node; may be {@code null} in which case 170 * {@code null} must be returned 171 * 172 * @param index the zero-based index of the child 173 * 174 * @return the child node of the supplied parent {@code node} 175 * identified by the supplied {@code name}, or {@code null} if 176 * either the child does not exist or {@code node} is not a 177 * {@linkplain #map(Object) map node} 178 * 179 * @exception NullPointerException if {@code name} is {@code null} 180 * 181 * @exception IndexOutOfBoundsException if {@code index} is invalid 182 * 183 * @nullability Overrides of this method may (and often do) return 184 * {@code null}. 185 * 186 * @idempotency Overrides of this method must be idempotent and 187 * deterministic. 188 * 189 * @threadsafety Overrides of this method must be safe for 190 * concurrent use by multiple threads. 191 */ 192 protected abstract N get(final N node, final int index); 193 194 /** 195 * Returns {@code true} if the supplied {@code node} is {@code 196 * null}, absent or synthetic. 197 * 198 * @param node the node to test; may be {@code null} in which case 199 * {@code true} must be returned 200 * 201 * @return {@code true} if the supplied {@code node} is {@code 202 * null}, absent or synthetic 203 * 204 * @idempotency Overrides of this method must be idempotent and 205 * deterministic. 206 * 207 * @threadsafety Overrides of this method must be safe for 208 * concurrent use by multiple threads. 209 */ 210 protected abstract boolean absent(final N node); 211 212 /** 213 * Returns {@code true} if the supplied {@code node} is {@code null} 214 * or represents an explicitly set {@code null} value. 215 * 216 * @param node the node to test; may be {@code null} in which case 217 * {@code true} must be returned 218 * 219 * @return {@code true} if the supplied {@code node} is {@code null} 220 * or represents an explicitly set {@code null} value 221 * 222 * @idempotency Overrides of this method must be idempotent and 223 * deterministic. 224 * 225 * @threadsafety Overrides of this method must be safe for 226 * concurrent use by multiple threads. 227 */ 228 protected abstract boolean nil(final N node); 229 230 /** 231 * Returns {@code true} if the supplied {@code node} represents a 232 * map, and therefore the {@link #get(Object, String)} method is 233 * likely to be relevant. 234 * 235 * @param node the node to test; may be {@code null} in which case 236 * {@code false} must be returned 237 * 238 * @return {@code true} if the supplied {@code node} represents a 239 * map, and therefore the {@link #get(Object, String)} method is 240 * likely to be relevant 241 * 242 * @idempotency Overrides of this method must be idempotent and 243 * deterministic. 244 * 245 * @threadsafety Overrides of this method must be safe for 246 * concurrent use by multiple threads. 247 */ 248 protected abstract boolean map(final N node); 249 250 /** 251 * Returns {@code true} if the supplied {@code node} represents a 252 * list or an array, and therefore the {@link #get(Object, int)} 253 * method is likely to be relevant. 254 * 255 * @param node the node to test; may be {@code null} in which case 256 * {@code false} must be returned 257 * 258 * @return {@code true} if the supplied {@code node} represents a 259 * list or an array, and therefore the {@link #get(Object, int)} 260 * method is likely to be relevant 261 * 262 * @idempotency Overrides of this method must be idempotent and 263 * deterministic. 264 * 265 * @threadsafety Overrides of this method must be safe for 266 * concurrent use by multiple threads. 267 */ 268 protected abstract boolean list(final N node); 269 270 /** 271 * Returns {@code true} if {@code node} is either a {@linkplain 272 * #map(Object) map} or a {@linkplain #list(Object) list or array}. 273 * 274 * @param node the node to test; may be {@code null} in which case 275 * {@code false} must be returned 276 * 277 * @return {@code true} if {@code node} is either a {@linkplain 278 * #map(Object) map} or a {@linkplain #list(Object) list or array} 279 * 280 * @idempotency Overrides of this method must be idempotent and 281 * deterministic. 282 * 283 * @threadsafety Overrides of this method must be safe for 284 * concurrent use by multiple threads. 285 */ 286 protected final boolean container(final N node) { 287 return !nil(node) && (map(node) || list(node)); 288 } 289 290 /** 291 * Returns a {@link BiFunction} accepting a node and a {@link Type} 292 * and returning the result of reading an object of that type, or 293 * {@code null} if no such {@link BiFunction} could be sourced. 294 * 295 * @param requestor the {@link Loader} currently executing a 296 * request; must not be {@code null} 297 * 298 * @param absolutePath the path being requested; must not be {@code 299 * null} and must be {@linkplain Path#absolute() absolute} 300 * 301 * @return a {@link BiFunction} accepting a node and a {@link Type} 302 * and returning the result of reading an object of that type, or 303 * {@code null} if no such {@link BiFunction} could be sourced 304 * 305 * @exception NullPointerException if any argument is {@code null} 306 * 307 * @nullability Overrides of this method may return {@code null}. 308 * 309 * @idempotency Overrides of this method must be idempotent and 310 * deterministic. 311 * 312 * @threadsafety Overrides of this method must be safe for 313 * concurrent use by multiple threads. 314 */ 315 protected abstract BiFunction<? super N, ? super Type, ?> reader(final Loader<?> requestor, 316 final Path<? extends Type> absolutePath); 317 318 /** 319 * Returns the root node of the tree that is suitable for the 320 * supplied {@link Loader} and {@link Path}, or {@code null} if no 321 * tree is suitable. 322 * 323 * @param requestor the {@link Loader} currently executing a 324 * request; must not be {@code null} 325 * 326 * @param absolutePath the path being requested; must not be {@code 327 * null} and must be {@linkplain Path#absolute() absolute} 328 * 329 * @return the root node, or {@code null} 330 * 331 * @exception NullPointerException if any argument is {@code null} 332 * 333 * @nullability Overrides of this method may return {@code null}. 334 * 335 * @idempotency Overrides of this method must be idempotent and 336 * deterministic. 337 * 338 * @threadsafety Overrides of this method must be safe for 339 * concurrent use by multiple threads. 340 */ 341 protected abstract N rootNode(final Loader<?> requestor, final Path<? extends Type> absolutePath); 342 343 /** 344 * Returns a node possibly containing qualifiers applicable to the 345 * supplied node, or {@code null}. 346 * 347 * @param node the node for which a corresponding qualifiers node 348 * should be returned; may be {@code null} in which case {@code 349 * null} must be returned 350 * 351 * @return a node possibly containing qualifiers applicable to the 352 * supplied node, or {@code null} 353 * 354 * @nullability Overrides of this method may (and often do) return 355 * {@code null} 356 * 357 * @idempotency Overrides of this method must be idempotent and 358 * deterministic. 359 * 360 * @threadsafety Overrides of this method must be safe for 361 * concurrent use by multiple threads. 362 */ 363 protected abstract N qualifiers(final N node); 364 365 /** 366 * Returns a {@linkplain FixedValueSupplier#of(Supplier) 367 * deterministic <code>Supplier</code>} suitable for the current 368 * request, represented by the supplied {@code absolutePath}, as 369 * being executed by the supplied {@link Loader}, or {@code null} if 370 * no such {@link Supplier} is or ever will be suitable. 371 * 372 * @param requestor the {@link Loader} currently executing a 373 * request; must not be {@code null} 374 * 375 * @param absolutePath the path being requested; must not be {@code 376 * null} and must be {@linkplain Path#absolute() absolute} 377 * 378 * @return a {@link Supplier}, or {@code null} 379 * 380 * @exception NullPointerException if any argument is {@code null} 381 * 382 * @idempotency No guarantees are made of either idempotency or 383 * determinism, either of this implementation or of its overrides. 384 * 385 * @threadsafety This method is, and its overrides must be, safe for 386 * concurrent use by multiple threads. 387 * 388 * @see Provider#get(Loader, Path) 389 */ 390 @Override // Provider 391 protected Supplier<?> find(final Loader<?> requestor, final Path<? extends Type> absolutePath) { 392 assert absolutePath.absolute(); 393 assert absolutePath.startsWith(requestor.path()); 394 assert !absolutePath.equals(requestor.path()); 395 396 final int size = absolutePath.size(); 397 assert size > 1; // follows from the above 398 399 N node = this.rootNode(requestor, absolutePath); 400 if (node != null) { 401 402 final BiFunction<? super N, ? super Type, ?> reader = this.reader(requestor, absolutePath); 403 if (reader != null) { 404 405 boolean containerNode = container(node); 406 407 for (int i = 1; i < size; i++) { // note that i is 1 on purpose; we skip the first root-designating element 408 final Element<?> element = absolutePath.get(i); 409 final String name = element.name(); 410 411 if (name.isEmpty()) { 412 // Empty name means "the current node". The node remains 413 // what it was. 414 continue; 415 } 416 417 if (!containerNode) { 418 // The name was non-empty, and the prior node was not a 419 // container, so we would be trying to dereference the 420 // name against a value node or a missing node, either of 421 // which is impossible. 422 node = null; 423 break; 424 } 425 426 if (!container(node)) { 427 // The next path element may have an empty name, in which 428 // case it will refer to this node, so the fact that it is 429 // not a container is still as of this moment OK. Record 430 // this fact so we can make sure on the next pass. 431 // valuePathElements.add(element); 432 containerNode = false; 433 continue; 434 } 435 436 if (list(node)) { 437 node = handleListNode(element, node); 438 } else if (map(node)) { 439 node = get(node, name); 440 } else { 441 throw new AssertionError(); 442 } 443 if (node == null) { 444 break; 445 } 446 447 } 448 449 if (node != null) { 450 return FixedValueSupplier.of(reader.apply(node, absolutePath.qualified())); 451 } 452 } 453 } 454 return null; 455 } 456 457 /** 458 * Calls the {@link #path(Loader, Path, BiFunction)} method with the 459 * supplied arguments and the return value of an invocation of the 460 * {@link #reader(Loader, Path)} method and returns its result. 461 * 462 * @param requestor the {@link Loader} currently executing a 463 * request; must not be {@code null} 464 * 465 * @param absolutePath the path being requested; must not be {@code 466 * null} and must be {@linkplain Path#absolute() absolute} 467 * 468 * @exception NullPointerException if {@code absolutePath} or {@code 469 * reader} is {@code null} 470 * 471 * @nullability This method does not return {@code null}. 472 * 473 * @idempotency No guarantees about idempotency or determinism are 474 * made of this method. 475 * 476 * @threadsafety This method is safe for concurrent use by multiple 477 * threads. 478 * 479 * @see #path(Loader, Path, BiFunction) 480 * 481 * @see #reader(Loader, Path) 482 */ 483 @Override 484 protected final <T extends Type> Path<T> path(final Loader<?> requestor, final Path<T> absolutePath) { 485 return this.path(requestor, absolutePath, this.reader(requestor, absolutePath)); 486 } 487 488 /** 489 * Returns a {@link Path} for a new {@link Value} that will be 490 * returned by the {@link #get(Loader, Path)} method. 491 * 492 * <p>The default implementation of this method attempts to build a 493 * {@link Path} with the same {@linkplain Path#size() sizze} as the 494 * supplied {@link Path} and differing, perhaps, only in the {@link 495 * Qualifiers} assigned to each {@link Element}.</p> 496 * 497 * <p>Overrides of this method must not call the {@link 498 * #path(Loader, Path)} or the {@link #get(Loader, Path)} methods or 499 * undefined behavior, such as an infinite loop, may result.</p> 500 * 501 * @param <T> the type of the supplied {@link Path} and the returned 502 * {@link Path} 503 * 504 * @param requestor the {@link Loader} currently executing a 505 * request; must not be {@code null} 506 * 507 * @param absolutePath the path being requested; must not be {@code 508 * null} and must be {@linkplain Path#absolute() absolute} 509 * 510 * @param reader a {@link BiFunction} accepting a node and a {@link 511 * Type} and returning the result of reading an object of that type; 512 * may be {@code null} 513 * 514 * @return a {@link Path}; never {@code null}; sometimes simply the 515 * supplied {@code absolutePath} 516 * 517 * @exception NullPointerException if {@code absolutePath} or {@code 518 * reader} is {@code null} 519 * 520 * @nullability This method does not, and its overrides must not, 521 * return {@code null}. 522 * 523 * @idempotency No guarantees about idempotency or determinism are 524 * made of this method or its overrides. 525 * 526 * @threadsafety This method is, and its overrides must be, safe for 527 * concurrent use by multiple threads. 528 */ 529 protected <T extends Type> Path<T> path(final Loader<?> requestor, 530 final Path<T> absolutePath, 531 final BiFunction<? super N, ? super Type, ?> reader) { 532 final int size = absolutePath.size(); 533 if (size <= 1) { 534 return absolutePath; 535 } 536 537 N node = this.rootNode(requestor, absolutePath); 538 if (node == null) { 539 return absolutePath; 540 } 541 542 Element<?> element = absolutePath.get(0); 543 assert element.isRoot(); 544 545 String nextName = absolutePath.get(1).name(); 546 547 final List<Element<?>> valuePathElements = new ArrayList<>(size); 548 valuePathElements.add(element); 549 550 N qualifiersNode = qualifiers(node); // often gets a child of this node named "@qualifiers" 551 final Qualifiers<? extends String, ?> valuePathQualifiers = this.qualifiers(reader, qualifiersNode, nextName); 552 qualifiersNode = this.get(qualifiersNode, nextName); 553 554 boolean containerNode = this.container(node); 555 556 for (int i = 1; i < size; i++) { 557 element = absolutePath.get(i); 558 final String name = element.name(); 559 560 if (name.isEmpty()) { 561 // Empty name means "the current node". The node remains what 562 // it was. 563 valuePathElements.add(element); 564 continue; 565 } 566 567 if (!containerNode) { 568 // The name was non-empty, and the prior node was not a 569 // container, so we would be trying to dereference the name 570 // against a value node or a missing node, either of which is 571 // impossible. 572 return absolutePath; 573 } 574 575 if (!this.container(node)) { 576 // The next path element may have an empty name, in which case 577 // it will refer to this node, so the fact that it is not a 578 // container is still as of this moment OK. Record this fact 579 // so we can make sure on the next pass. 580 valuePathElements.add(element); 581 containerNode = false; 582 continue; 583 } 584 585 final boolean hasNext = i + 1 < size; 586 nextName = hasNext ? absolutePath.get(i + 1).name() : null; 587 valuePathElements.add(Element.of(qualifiers(reader, qualifiersNode, nextName), 588 element.qualified(), 589 name)); 590 qualifiersNode = this.get(qualifiersNode, nextName); 591 } 592 @SuppressWarnings("unchecked") 593 final Element<T> last = (Element<T>)valuePathElements.remove(valuePathElements.size() - 1); 594 return Path.of(valuePathQualifiers, valuePathElements, last); 595 } 596 597 /** 598 * Returns a {@link Qualifiers} derived from the supplied {@code 599 * qualifiersNode}. 600 * 601 * @param reader a {@link BiFunction} accepting a node and a {@link 602 * Type} and returning the result of reading an object of that type; 603 * may be {@code null} 604 * 605 * @param qualifiersNode a node {@linkplain #names(Object) 606 * containing} qualifier entries; may be {@code null} 607 * 608 * @param nextPathElementName the name of the next path element; may 609 * be {@code null} 610 * 611 * @return a {@link Qualifiers}; never {@code null} 612 * 613 * @nullability This method does not, and its overrides must not, 614 * return {@code null}. 615 * 616 * @idempotency This method is, and its overrides must be, 617 * idempotent and deterministic. 618 * 619 * @threadsafety This method is, and its overrides must be, safe for 620 * concurrent use by multiple threads. 621 */ 622 protected Qualifiers<? extends String, ?> qualifiers(final BiFunction<? super N, ? super Type, ?> reader, 623 final N qualifiersNode, 624 final String nextPathElementName) { 625 if (qualifiersNode == null || reader == null || size(qualifiersNode) <= 0) { 626 return Qualifiers.of(); 627 } else { 628 final Collection<Qualifier<String, Object>> qualifiersSet; 629 final Iterator<? extends String> fieldNamesIterator = names(qualifiersNode); 630 if (fieldNamesIterator.hasNext()) { 631 qualifiersSet = new TreeSet<>(); 632 while (fieldNamesIterator.hasNext()) { 633 final String qualifierKey = fieldNamesIterator.next(); 634 final N qualifierValueNode = get(qualifiersNode, qualifierKey); 635 assert qualifierValueNode != null; // ...or it shouldn't have been iterated 636 assert !absent(qualifierValueNode); // never returned by get() 637 if (!map(qualifierValueNode) || !qualifierKey.equals(nextPathElementName)) { 638 final Object qualifierValue = reader.apply(qualifierValueNode, Object.class); 639 if (qualifierValue != null) { 640 qualifiersSet.add(Qualifier.of(qualifierKey, qualifierValue)); 641 } 642 } 643 } 644 } else { 645 qualifiersSet = List.of(); 646 } 647 return qualifiersSet.isEmpty() ? Qualifiers.of() : Qualifiers.of(qualifiersSet); 648 } 649 } 650 651 private final N handleListNode(final Element<?> element, N node) { 652 String key = null; 653 final Qualifiers<String, Object> qs = element.qualifiers(); 654 for (final Qualifier<? extends String, ?> q : qs) { 655 final String name = q.name(); 656 if (key == null) { 657 if (name.equals("index")) { 658 key = "index"; 659 } else if (name.equals("arg0")) { 660 key = "arg0"; 661 } 662 } 663 if (key != null) { 664 int index = -1; 665 final Object value = q.value(); 666 if (value instanceof Number n) { 667 index = n.intValue(); 668 } else { 669 try { 670 index = Integer.parseInt(value.toString()); 671 } catch (final NumberFormatException numberFormatException) { 672 break; 673 } 674 } 675 if (index >= 0) { 676 return get(node, index); 677 } 678 } 679 } 680 return null; 681 } 682 683}