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.GenericArrayType;
020import java.lang.reflect.ParameterizedType;
021import java.lang.reflect.Type;
022
023import java.util.NoSuchElementException;
024import java.util.Objects;
025
026import java.util.function.Supplier;
027
028import org.microbean.development.annotation.Convenience;
029
030import org.microbean.invoke.FixedValueSupplier;
031import org.microbean.invoke.OptionalSupplier;
032
033import org.microbean.path.Path;
034
035import org.microbean.qualifier.Qualifiers;
036
037import org.microbean.type.JavaTypes;
038
039/**
040 * An {@link OptionalSupplier} of a value that is additionally
041 * qualified by a {@link Path} partially identifying the kinds of
042 * {@link Qualifiers} and {@link Path}s for which it might be
043 * suitable.
044 *
045 * <p>{@link Value}s are typically returned by {@link Provider}
046 * implementations.</p>
047 *
048 * <p>A {@link Value} once received retains no reference to whatever
049 * produced it and can be regarded as an authoritative source for
050 * (possibly ever-changing) values going forward.  Notably, it can be
051 * cached.</p>
052 *
053 * @param <T> the type of value this {@link Value} returns
054 *
055 * @author <a href="https://about.me/lairdnelson"
056 * target="_parent">Laird Nelson</a>
057 *
058 * @see OptionalSupplier
059 *
060 * @see Provider
061 */
062public final class Value<T> implements OptionalSupplier<T> {
063
064
065  /*
066   * Instance fields.
067   */
068
069
070  private final Path<? extends Type> path;
071
072  private final OptionalSupplier<? extends T> supplier;
073
074
075  /*
076   * Constructors.
077   */
078
079
080  /**
081   * Creates a new {@link Value} <strong>that returns a valid {@code
082   * null} value from its {@link #get() get()} method</strong>.
083   *
084   * @param path the {@link Path}, possibly relative, for which this
085   * {@link Value} is suitable; must not be {@code null}
086   *
087   * @exception NullPointerException if {@code path} is {@code null}
088   *
089   * @see #get()
090   *
091   * @see FixedValueSupplier#of(Object)
092   */
093  public Value(final Path<? extends Type> path) {
094    this(FixedValueSupplier.of(null), path);
095  }
096
097  /**
098   * Creates a new {@link Value} that will forever and always return
099   * the supplied {@code value} from its {@link #get() get()} method.
100   *
101   * @param path the {@link Path}, possibly relative, for which this
102   * {@link Value} is suitable; must not be {@code null}
103   *
104   * @param value a fixed value to be returned by the {@link #get()}
105   * method whenever it is invoked; may be {@code null}
106   *
107   * @exception NullPointerException if {@code path} is {@code null}
108   *
109   * @see #get()
110   *
111   * @see FixedValueSupplier#of(Object)
112   */
113  public Value(final T value, final Path<? extends Type> path) {
114    this(FixedValueSupplier.of(value), path);
115  }
116
117  /**
118   * Creates a new {@link Value}.
119   *
120   * @param source the {@link Value} to use as the primary supplier;
121   * must not be {@code null}
122   *
123   * @param defaults the {@link Supplier} to use as the fallback; may
124   * be {@code null}
125   *
126   * @exception NullPointerException if {@code source} is {@code null}
127   */
128  public Value(final Value<? extends T> source, final Supplier<? extends T> defaults) {
129    this(OptionalSupplier.of(source, defaults), source.path());
130  }
131
132  /**
133   * Creates a new {@link Value}.
134   *
135   * @param supplier the actual {@link Supplier} that will return
136   * values; may be {@code null}
137   *
138   * @param path the {@link Path}, possibly relative, for which this
139   * {@link Value} is suitable; must not be {@code null}
140   *
141   * @exception NullPointerException if {@code path} is {@code null}
142   */
143  public Value(final Supplier<? extends T> supplier, final Path<? extends Type> path) {
144    super();
145    this.supplier = OptionalSupplier.of(supplier);
146    this.path = Objects.requireNonNull(path, "path");
147  }
148
149
150  /*
151   * Instance methods.
152   */
153
154
155  /**
156   * Returns a {@link Value} with this {@link Value}'s supplier and
157   * the supplied {@link Path}.
158   *
159   * @param path the new {@link Path}; must not be {@code null}
160   *
161   * @return a {@link Value} with this {@link Value}'s supplier and
162   * the supplied {@link Path}
163   *
164   * @exception NullPointerException if {@code path} is {@code null}
165   *
166   * @nullability This method never returns {@code null}.
167   *
168   * @idempotency This method is idempotent and deterministic.
169   *
170   * @threadsafety This method is safe for concurrent use by multiple
171   * threads.
172   */
173  public final Value<T> with(final Path<? extends Type> path) {
174    if (path.equals(this.path())) {
175      return this;
176    } else {
177      return new Value<>(this.supplier, path);
178    }
179  }
180
181  /**
182   * Returns the {@link Qualifiers} with which this {@link Value} is
183   * associated.
184   *
185   * @return the {@link Qualifiers} with which this {@link Value} is
186   * associated
187   *
188   * @nullability This method never returns {@code null}.
189   *
190   * @idempotency This method is idempotent and deterministic.
191   *
192   * @threadsafety This method is safe for concurrent use by multiple
193   * threads.
194   *
195   * @see Path#qualifiers()
196   */
197  @Convenience
198  public final Qualifiers<? extends String, ?> qualifiers() {
199    return this.path().qualifiers();
200  }
201
202  /**
203   * Returns the {@link Path} with which this {@link Value} is
204   * associated.
205   *
206   * @return the {@link Path} with which this {@link Value} is
207   * associated
208   *
209   * @nullability This method never returns {@code null}.
210   *
211   * @idempotency This method is idempotent and deterministic.
212   *
213   * @threadsafety This method is safe for concurrent use by multiple
214   * threads.
215   */
216  public final Path<? extends Type> path() {
217    return this.path;
218  }
219
220  /**
221   * Invokes the {@link Supplier#get() get()} method of the {@link
222   * Supplier} supplied at {@linkplain #Value(Supplier, Path)
223   * construction time} and returns its value, which may be {@code
224   * null}.
225   *
226   * @return tbe return value of an invocation of the {@link
227   * OptionalSupplier#get() get()} method of the {@link Supplier}
228   * supplied at {@linkplain #Value(Supplier, Path) construction
229   * time}, which may be {@code null}
230   *
231   * @exception NoSuchElementException if this method should no longer
232   * be invoked because there is no chance it will ever produce a
233   * suitable value again
234   *
235   * @exception UnsupportedOperationException if this method should no
236   * longer be invoked because there is no chance it will ever produce
237   * a suitable value again
238   *
239   * @see #Value(Supplier, Path)
240   *
241   * @nullability This method may return {@code null}.
242   *
243   * @threadsafety This method is safe for concurrent use by multiple
244   * threads, provided that the {@link Supplier} supplied at
245   * {@linkplain #Value(Supplier, Path) construction time} is also
246   * safe for concurrent use by multiple threads.
247   *
248   * @idempotency This method is as idempotent and deterministic as
249   * the {@link Supplier} supplied at {@linkplain #Value(Supplier,
250   * Path) construction time}.
251   */
252  @Override // Supplier<T>
253  public final T get() {
254    return this.supplier.get();
255  }
256
257  /**
258   * Returns an appropriate {@link Determinism} for this {@link
259   * Value}.
260   *
261   * @return an appropriate {@link Determinism} for this {@link Value}
262   *
263   * @nullability This method never returns {@code null}.
264   *
265   * @idempotency This method is idempotent and deterministic.
266   *
267   * @threadsafety This method is safe for concurrent use by multiple
268   * threads.
269   */
270  @Override // OptionalSupplier<T>
271  public final Determinism determinism() {
272    return this.supplier.determinism();
273  }
274
275  /**
276   * Returns the result of invoking {@link Path#qualified()} on the
277   * return value of this {@link Value}'s {@link #path()} method.
278   *
279   * <p>This method never returns {@code null}.</p>
280   *
281   * @return the result of invoking {@link Path#qualified()} on the
282   * return value of this {@link Value}'s {@link #path()} method;
283   * never {@code null}
284   *
285   * @nullability This method never returns {@code null}.
286   *
287   * @idempotency This method is idempotent and and deterministic.
288   *
289   * @threadsafety This method is safe for concurrent use by
290   * multiple threads.
291   *
292   * @see #path()
293   *
294   * @see Path#qualified()
295   */
296  @Convenience
297  public final Type type() {
298    return this.path().qualified();
299  }
300
301  /**
302   * Returns the type erasure of the return value of the {@link
303   * #type()} method.
304   *
305   * <p>This method never returns {@code null}.</p>
306   *
307   * @return the result of invoking {@link JavaTypes#erase(Type)} on
308   * the return value of this {@link Value}'s {@link #type()} method;
309   * never {@code null}
310   *
311   * @exception IllegalStateException if somehow the return value of
312   * {@link #type()} could not be erased
313   *
314   * @nullability This method never returns {@code null}.
315   *
316   * @idempotency This method is idempotent and and deterministic.
317   *
318   * @threadsafety This method is safe for concurrent use by
319   * multiple threads.
320   *
321   * @see #path()
322   *
323   * @see JavaTypes#erase(Type)
324   */
325  public final Class<T> typeErasure() {
326    @SuppressWarnings("unchecked")
327    final Class<T> returnValue = (Class<T>)JavaTypes.erase(this.type());
328    if (returnValue == null) {
329      throw new IllegalStateException();
330    }
331    return returnValue;
332  }
333
334  /**
335   * Returns a hashcode for this {@link Value} calculated from its
336   * {@link #path() Path}, and {@linkplain #determinism() whether it
337   * is deterministic}.
338   *
339   * @return a hashcode for this {@link Value}
340   *
341   * @idempotency This method is idempotent and and deterministic.
342   *
343   * @threadsafety This method is safe for concurrent use by
344   * multiple threads.
345   *
346   * @see #equals(Object)
347   *
348   * @see #path()
349   *
350   * @see #determinism()
351   */
352  @Override // Object
353  public final int hashCode() {
354    int hashCode = 17;
355
356    Object v = this.path();
357    int c = v == null ? 0 : v.hashCode();
358    hashCode = 37 * hashCode + c;
359
360    v = this.determinism();
361    c = v == null ? 0 : v.hashCode();
362    hashCode = 37 * hashCode + c;
363
364    return hashCode;
365  }
366
367  /**
368   * Returns {@code true} if this {@link Value} is equal to the
369   * supplied {@link Object}.
370   *
371   * <p>This method will return {@code true} if and only if the
372   * following conditions hold:</p>
373   *
374   * <ul>
375   *
376   * <li>The supplied {@link Object} is not {@code null}</li>
377   *
378   * <li>The supplied {@link Object}'s {@link Object#getClass()}
379   * method returns {@link Value Value.class}</li>
380   *
381   * <li>{@link Objects#equals(Object, Object)
382   * Objects.equals(this.path(), otherValue.path())} returns {@code
383   * true}</li>
384   *
385   * <li>{@link Objects#equals(Object, Object)
386   * Objects.equals(this.determinism(), otherValue.determinism())}
387   * returns {@code true}</li>
388   *
389   * </ul>
390   *
391   * @param other the {@link Object} to test; may be {@code null} in
392   * which case {@code false} will be returned
393   *
394   * @return {@code true} if this {@link Value} is equal to the
395   * supplied {@link Object}; {@code false} otherwise
396   *
397   * @idempotency This method is idempotent and and deterministic.
398   *
399   * @threadsafety This method is safe for concurrent use by
400   * multiple threads.
401   *
402   * @see #path()
403   *
404   * @see #determinism()
405   */
406  @Override // Object
407  public final boolean equals(final Object other) {
408    if (other == this) {
409      return true;
410    } else if (other != null && this.getClass() == other.getClass()) {
411      final Value<?> her = (Value<?>)other;
412      return
413        Objects.equals(this.path(), her.path()) &&
414        Objects.equals(this.determinism(), her.determinism());
415    } else {
416      return false;
417    }
418  }
419
420}