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.invoke;
018
019import java.util.NoSuchElementException;
020import java.util.Optional;
021
022import java.util.concurrent.atomic.AtomicReference;
023
024import java.util.function.Supplier;
025
026/**
027 * An {@link OptionalSupplier} that computes the value it will
028 * return from its {@link #get()} method when that method is first
029 * invoked, and that returns that computed value for all subsequent
030 * invocations of that method.
031 *
032 * @author <a href="https://about.me/lairdnelson"
033 * target="_parent">Laird Nelson</a>
034 *
035 * @param <T> The type of object returned by the {@link #get()}
036 * method
037 *
038 * @see #CachingSupplier(Object)
039 *
040 * @see #CachingSupplier(Supplier)
041 *
042 * @see #get()
043 *
044 * @see #set(Object)
045 */
046public final class CachingSupplier<T> implements OptionalSupplier<T> {
047
048
049  /*
050   * Instance fields.
051   */
052
053
054  private final Supplier<? extends T> delegate;
055
056  private final AtomicReference<Optional<T>> ref;
057
058
059  /*
060   * Constructors.
061   */
062
063
064  /**
065   * Creates a new {@link CachingSupplier}.
066   *
067   * @see #CachingSupplier(Supplier)
068   *
069   * @see #get()
070   *
071   * @see #set(Object)
072   *
073   * @see
074   * AtomicReference#updateAndGet(java.util.function.UnaryOperator)
075   */
076  public CachingSupplier() {
077    this((Supplier<? extends T>)null);
078  }
079
080  /**
081   * Creates a new {@link CachingSupplier}.
082   *
083   * <p>An invocation of this constructor will result in the {@link
084   * #set(Object)} method always returning {@code false}.</p>
085   *
086   * @param value the value that will be returned by all invocations
087   * of the {@link #get()} method; may be {@code null} in which case
088   * the {@link #get()} method will return {@code null} forever
089   *
090   * @see #get()
091   */
092  public CachingSupplier(final T value) {
093    super();
094    final Optional<T> optional = Optional.ofNullable(value);
095    this.ref = new AtomicReference<>(optional);
096    this.delegate = OptionalSupplier.of(optional.orElse(null));
097  }
098
099  /**
100   * Creates a new {@link CachingSupplier}.
101   *
102   * @param supplier the {@link Supplier} that will be used to supply
103   * the value that will be returned by all invocations of the {@link
104   * #get()} method; may be {@code null} in which case the {@link
105   * #get()} method will throw a {@link NoSuchElementException} until,
106   * at least, the {@link #set(Object)} method is called; <strong>must
107   * be safe for concurrent use by multiple threads and must be
108   * side-effect free</strong>
109   *
110   * @see #get()
111   */
112  public CachingSupplier(final Supplier<? extends T> supplier) {
113    super();
114    this.ref = new AtomicReference<>();
115    this.delegate = supplier == null ? CachingSupplier::noSuchElementException : supplier;
116  }
117
118
119  /*
120   * Instance methods.
121   */
122
123
124  /**
125   * Returns the value this {@link CachingSupplier} will forever
126   * supply, computing it if necessary with the first invocation by
127   * using the {@link Supplier} supplied at {@linkplain
128   * #CachingSupplier(Supplier) construction time}.
129   *
130   * <p>If the {@link Supplier} supplied at {@linkplain
131   * #CachingSupplier(Supplier) construction time} returns {@code
132   * null} from its {@link Supplier#get()} method, then this method
133   * will forever return {@code null} as well.</p>
134   *
135   * @return the value, which may very well be {@code null}
136   *
137   * @nullability This method may return {@code null}.
138   *
139   * @idempotency This method's idempotency and determinism are
140   * determined by the idempotency and determinisim of the {@link
141   * Supplier} supplied at {@linkplain #CachingSupplier(Supplier)
142   * construction time}.
143   *
144   * @threadsafety This method is safe for concurrent use by multiple
145   * threads.
146   *
147   * @see #CachingSupplier(Object)
148   *
149   * @see #CachingSupplier(Supplier)
150   *
151   * @see #set(Object)
152   */
153  @Override // Supplier<T>
154  public final T get() {
155    Optional<T> optional = this.ref.get();
156    if (optional == null) {
157      optional = Optional.ofNullable(this.delegate.get());
158      if (!this.ref.compareAndSet(null, optional)) {
159        optional = this.ref.get();
160      }
161    }
162    return optional.orElse(null);
163  }
164
165  /**
166   * Sets the value that will be returned forever afterwards by the
167   * {@link #get()} method and returns {@code true} if and only if the
168   * value was previously unset.
169   *
170   * @param newValue the new value that will be returned by the {@link
171   * #get()} method forever afterwards; may be {@code null}
172   *
173   * @return {@code true} if and only if this assignment was permitted;
174   * {@code false} otherwise
175   *
176   * @idempotency This method is idempotent but not deterministic.
177   *
178   * @threadsafety This method is safe for concurrent use by multiple
179   * threads.
180   *
181   * @see #get()
182   *
183   * @see #CachingSupplier(Supplier)
184   *
185   * @see AtomicReference#compareAndSet(Object, Object)
186   */
187  public final boolean set(final T newValue) {
188    return this.ref.compareAndSet(null, Optional.ofNullable(newValue));
189  }
190
191  /**
192   * Returns an {@link Determinism} suitable for this {@link CachingSupplier}.
193   *
194   * <p>In most cases, this method returns {@link
195   * Determinism#PRESENT}.  In the case that the {@linkplain
196   * #CachingSupplier() zero-argument constructor} was invoked, and
197   * the {@link #set(Object)} method has not yet been called, this
198   * method will return {@link Determinism#DETERMINISTIC}.  Once the
199   * {@link #set(Object)} method has been called, this method will
200   * return {@link Determinism#PRESENT}.</p>
201   *
202   * @return one of {@link Determinism#PRESENT} or {@link
203   * Determinism#DETERMINISTIC}
204   *
205   * @nullability This method does not return {@code null}.
206   *
207   * @idempotency This method is idempotent and deterministic.
208   *
209   * @threadsafety This method is safe for concurrent use by multiple
210   * threads.
211   */
212  @Override
213  public final Determinism determinism() {
214    return this.ref.get() == null ? Determinism.DETERMINISTIC : Determinism.PRESENT;
215  }
216
217  /**
218   * Returns a non-{@code null} {@link String} representation of this
219   * {@link CachingSupplier}.
220   *
221   * <p>The format of the return value is deliberately unspecified and
222   * subject to change without notice from version to version of this
223   * class.</p>
224   *
225   * @return a {@link String} representation of this {@link
226   * CachingSupplier}
227   *
228   * @nullability This method never returns {@code null}.
229   *
230   * @idempotency This method is idempotent but not deterministic
231   * until a value has been supplied, either via the {@link
232   * #CachingSupplier(Object)} or {@link #CachingSupplier(Supplier)}
233   * constructors or the {@link #set(Object)} method.
234   *
235   * @threadsafety This method is safe for concurrent use by multiple
236   * threads.
237   */
238  @Override // Object
239  public final String toString() {
240    return String.valueOf(this.get());
241  }
242
243
244  /*
245   * Static methods.
246   */
247
248
249  private static final <T> T noSuchElementException() {
250    throw new NoSuchElementException();
251  }
252
253}