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}