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.ParameterizedType;
020import java.lang.reflect.Type;
021
022import java.util.function.Supplier;
023
024import org.microbean.development.annotation.OverridingEncouraged;
025
026import org.microbean.loader.api.Loader;
027
028import org.microbean.path.Path;
029
030/**
031 * A skeletal {@link Provider} implementation.
032 *
033 * @author <a href="https://about.me/lairdnelson"
034 * target="_parent">Laird Nelson</a>
035 *
036 * @see #lowerBound()
037 *
038 * @see Provider
039 */
040public abstract class AbstractProvider implements Provider {
041
042
043  /*
044   * Instance fields.
045   */
046
047
048  private final Type lowerBound;
049
050
051  /*
052   * Constructors.
053   */
054
055
056  /**
057   * Creates a new {@link AbstractProvider} with {@code null} as its
058   * {@linkplain #lowerBound() lower type bound}.
059   *
060   * @see #AbstractProvider(Type)
061   *
062   * @see #lowerBound()
063   */
064  protected AbstractProvider() {
065    this(null);
066  }
067
068  /**
069   * Creates a new {@link AbstractProvider}.
070   *
071   * @param lowerBound the {@linkplain #lowerBound() lower type bound}
072   * of this {@link AbstractProvider}; may be, and often is, {@code
073   * null}
074   *
075   * @see #lowerBound()
076   */
077  protected AbstractProvider(final Type lowerBound) {
078    super();
079    this.lowerBound = lowerBound;
080  }
081
082
083  /*
084   * Instance methods.
085   */
086
087
088  /**
089   * Returns a {@link Type} representing the <strong>lower bound of
090   * all possible {@linkplain Value values}</strong> {@linkplain
091   * #get(Loader, Path) supplied} by this {@link AbstractProvider}.
092   *
093   * @return the lower type bound of this {@link AbstractProvider}
094   * implementation, or {@code null} if its lower bound is the lowest
095   * possible bound
096   *
097   * @nullability This method may return {@code null}.
098   *
099   * @idempotency This method is idempotent and deterministic.
100   *
101   * @threadsafety This method is safe for concurrent use by multiple
102   * threads.
103   */
104  @Override // Provider
105  public final Type lowerBound() {
106    return this.lowerBound;
107  }
108
109  /**
110   * Implements the {@link Provider#get(Loader, Path)} method to first
111   * call the {@link #find(Loader, Path)} method, and, if necessary,
112   * the {@link #path(Loader, Path)} method, and to then return a
113   * {@link Value} made up of the relevant return values.
114   *
115   * @param requestor the {@link Loader} seeking a {@link Value};
116   * must not be {@code null}
117   *
118   * @param absolutePath an {@linkplain Path#absolute() absolute
119   * <code>Path</code>} for which the supplied {@link Loader} is
120   * seeking a value; must not be {@code null}
121   *
122   * @return a {@link Value} more or less suitable for the combination
123   * of the supplied {@link Loader} and {@link Path}, or {@code
124   * null} if there is no such {@link Value} now <strong>and if there
125   * never will be such a {@link Value}</strong> for the supplied
126   * arguments
127   *
128   * @exception NullPointerException if either {@code requestor} or
129   * {@code absolutePath} is {@code null}
130   *
131   * @exception IllegalArgumentException if {@code absolutePath}
132   * {@linkplain Path#absolute() is not absolute}, or if {@link
133   * Path#startsWith(Path)
134   * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link
135   * Path#equals(Object)
136   * absolutePath.equals(requestor.absolutePath())}
137   *
138   * @nullability This method may return {@code null}.
139   *
140   * @idempotency This method is idempotent, but not necessarily
141   * deterministic.
142   *
143   * @threadsafety This method is safe for concurrent use by multiple
144   * threads.
145   *
146   * @see Provider#get(Loader, Path)
147   *
148   * @see #find(Loader, Path)
149   *
150   * @see #path(Loader, Path)
151   */
152  @Override // Provider
153  public final Value<?> get(final Loader<?> requestor, final Path<? extends Type> absolutePath) {
154    final Supplier<?> s = this.find(requestor, absolutePath);
155    if (s != null) {
156      if (s instanceof Value<?> v) {
157        return v;
158      }
159      final Path<? extends Type> p = this.path(requestor, absolutePath);
160      if (p != null) {
161        return new Value<>(s, p);
162      }
163    }
164    return null;
165  }
166
167  /**
168   * Returns a {@link Supplier} suitable for the supplied {@link
169   * Loader} and {@link Path}, or {@code null} if there is no such
170   * {@link Supplier} now <strong>and if there never will be such a
171   * {@link Supplier}</strong> for the supplied arguments.
172   *
173   * <p>Overrides of this method must not call the {@link #get(Loader,
174   * Path)} method or undefined behavior (such as an infinite loop)
175   * may result.</p>
176   *
177   * @param requestor the {@link Loader} seeking a {@link Value} (as
178   * originally passed to the {@link #get(Loader, Path)} method); must
179   * not be {@code null}
180   *
181   * @param absolutePath an {@linkplain Path#absolute() absolute
182   * <code>Path</code>} for which the supplied {@link Loader} is
183   * seeking a value (as originally passed to the {@link #get(Loader,
184   * Path)} method); must not be {@code null}
185   *
186   * @return a {@link Supplier} more or less suitable for the
187   * combination of the supplied {@link Loader} and {@link Path}, or
188   * {@code null} if there is no such {@link Supplier} now <strong>and
189   * if there never will be such a {@link Supplier}</strong> for the
190   * supplied arguments
191   *
192   * @exception NullPointerException if either {@code requestor} or
193   * {@code absolutePath} is {@code null}
194   *
195   * @exception IllegalArgumentException if {@code absolutePath}
196   * {@linkplain Path#absolute() is not absolute}, or if {@link
197   * Path#startsWith(Path)
198   * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link
199   * Path#equals(Object)
200   * absolutePath.equals(requestor.absolutePath())}
201   *
202   * @nullability Overrides of this method may return {@code null}, in
203   * which case the {@link #get(Loader, Path)} method will return
204   * {@code null} as well.
205   *
206   * @idempotency Overrides of this method must be idempotent but are
207   * not assumed to be deterministic.
208   *
209   * @threadsafety Overrides of this method must be safe for
210   * concurrent use by multiple threads.
211   *
212   * @see #get(Loader, Path)
213   */
214  protected abstract Supplier<?> find(final Loader<?> requestor, final Path<? extends Type> absolutePath);
215
216  /**
217   * Returns a {@link Path} that will be {@linkplain
218   * Value#Value(Supplier, Path) used to build a <code>Value</code>}
219   * to be returned by the {@link #get(Loader, Path)} method.
220   *
221   * <p>This method does not, and its overrides must not, call the
222   * {@link #get(Loader, Path)} method or undefined behavior (such as
223   * an infinite loop) may result.</p>
224   *
225   * <p>The default implementation of this method returns the supplied
226   * {@code absolutePath}.</p>
227   *
228   * <p>This method is called by the {@link #get(Loader, Path)} method
229   * only when a call to the {@link #find(Loader, Path)} method
230   * returns a non-{@code null} value.</p>
231   *
232   * @param <T> the type of both the supplied and returned {@link
233   * Path}s
234   *
235   * @param requestor requestor the {@link Loader} seeking a {@link
236   * Value} (as originally passed to the {@link #get(Loader, Path)}
237   * method); must not be {@code null}
238   *
239   * @param absolutePath an {@linkplain Path#absolute() absolute
240   * <code>Path</code>} for which the supplied {@link Loader} is
241   * seeking a value (as originally passed to the {@link #get(Loader,
242   * Path)} method); must not be {@code null}
243   *
244   * @return a {@link Path} that will be {@linkplain
245   * Value#Value(Supplier, Path) used to build a <code>Value</code>}
246   * to be returned by the {@link #get(Loader, Path)} method; if
247   * {@code null}, then the {@link #get(Loader, Path)} method will
248   * return {@code null} as well
249   *
250   * @exception NullPointerException if either {@code requestor} or
251   * {@code absolutePath} is {@code null}
252   *
253   * @exception IllegalArgumentException if {@code absolutePath}
254   * {@linkplain Path#absolute() is not absolute}, or if {@link
255   * Path#startsWith(Path)
256   * !absolutePath.startsWith(requestor.absolutePath())}, or if {@link
257   * Path#equals(Object)
258   * absolutePath.equals(requestor.absolutePath())}
259   *
260   * @nullability This method does not, but its overrides may, return
261   * {@code null}.
262   *
263   * @idempotency This method is, and its overrides must be,
264   * idempotent and deterministic.
265   *
266   * @threadsafety This method is, and its overrides must be, safe for
267   * concurrent use by multiple threads.
268   *
269   * @see #get(Loader, Path)
270   */
271  @OverridingEncouraged
272  protected <T extends Type> Path<T> path(final Loader<?> requestor, final Path<T> absolutePath) {
273    return absolutePath;
274  }
275
276}