001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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 implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.function;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import java.util.*;
023import java.util.concurrent.atomic.*;
024import java.util.function.*;
025
026/**
027 * A thread-safe supplier that caches the result of the first call and supports resetting the cache.
028 *
029 * <p>
030 * This class extends {@link OptionalSupplier} to provide both standard {@link Supplier#get()} functionality
031 * and a {@link #reset()} method to clear the cache, forcing recomputation on the next call to {@link #get()}.
032 * It also inherits all Optional-like convenience methods from {@link OptionalSupplier}.
033 *
034 * <h5 class='section'>Usage:</h5>
035 * <p class='bjava'>
036 *    <jc>// Create a resettable supplier</jc>
037 *    ResettableSupplier&lt;String&gt; <jv>supplier</jv> = <jk>new</jk> ResettableSupplier&lt;&gt;(() -&gt; expensiveComputation());
038 *
039 *    <jc>// First call computes and caches</jc>
040 *    String <jv>result1</jv> = <jv>supplier</jv>.get();
041 *
042 *    <jc>// Subsequent calls return cached value</jc>
043 *    String <jv>result2</jv> = <jv>supplier</jv>.get();
044 *
045 *    <jc>// Use Optional-like methods</jc>
046 *    <jk>if</jk> (<jv>supplier</jv>.isPresent()) {
047 *       String <jv>upper</jv> = <jv>supplier</jv>.map(String::toUpperCase).orElse(<js>"default"</js>);
048 *    }
049 *
050 *    <jc>// Reset forces recomputation on next get()</jc>
051 *    <jv>supplier</jv>.reset();
052 *    String <jv>result3</jv> = <jv>supplier</jv>.get();  <jc>// Recomputes</jc>
053 * </p>
054 *
055 * <h5 class='section'>Thread Safety:</h5>
056 * <p>
057 * This class is thread-safe for both {@link #get()} and {@link #reset()} operations.
058 * If multiple threads call get() simultaneously after a reset, the supplier may be invoked
059 * multiple times, but only one result will be cached.
060 *
061 * <h5 class='section'>Notes:</h5>
062 * <ul>
063 *    <li>The supplier may be called multiple times if threads race, but only one result is cached.
064 *    <li>The cached value can be <jk>null</jk> if the supplier returns <jk>null</jk>.
065 *    <li>After reset, the next get() will recompute the value.
066 *    <li>This is particularly useful for testing when configuration changes and cached values need to be recalculated.
067 *    <li>Inherits all Optional-like methods from {@link OptionalSupplier} (isPresent(), isEmpty(), map(), orElse(), etc.).
068 * </ul>
069 *
070 * <h5 class='section'>See Also:</h5>
071 * <ul>
072 *    <li class='jm'>{@link org.apache.juneau.commons.utils.Utils#memr(Supplier)}
073 *    <li class='jc'>{@link OptionalSupplier} - Base interface providing Optional-like methods
074 * </ul>
075 *
076 * @param <T> The type of value supplied.
077 */
078public class ResettableSupplier<T> implements OptionalSupplier<T> {
079   private final Supplier<T> supplier;
080   private final AtomicReference<Optional<T>> cache = new AtomicReference<>();
081
082   /**
083    * Constructor.
084    *
085    * @param supplier The underlying supplier to call when computing values.  Must not be <jk>null</jk>.
086    */
087   public ResettableSupplier(Supplier<T> supplier) {
088      this.supplier = assertArgNotNull("supplier", supplier);
089   }
090
091   /**
092    * Returns the cached value if present, otherwise computes it using the underlying supplier.
093    *
094    * @return The cached or newly computed value.
095    */
096   @Override
097   public T get() {
098      Optional<T> h = cache.get();
099      if (h == null) {
100         h = opt(supplier.get());
101         if (! cache.compareAndSet(null, h)) {
102            // Another thread beat us, use their value
103            h = cache.get();
104         }
105      }
106      return h.orElse(null);
107   }
108
109   /**
110    * Clears the cached value, forcing the next call to {@link #get()} to recompute the value.
111    *
112    * <p>
113    * This method is thread-safe and can be called from multiple threads. After reset,
114    * the next get() call will invoke the underlying supplier to compute a fresh value.
115    */
116   public void reset() {
117      cache.set(null);
118   }
119
120   /**
121    * Sets the cached value directly without invoking the underlying supplier.
122    *
123    * <p>
124    * This method allows you to override the cached value, bypassing the supplier.
125    * Subsequent calls to {@link #get()} will return the set value until {@link #reset()} is called
126    * or the value is set again.
127    *
128    * <p>
129    * This method is thread-safe and is particularly useful for testing when you need to
130    * inject a specific value without invoking the supplier.
131    *
132    * <h5 class='section'>Example:</h5>
133    * <p class='bjava'>
134    *    <jc>// Create a supplier</jc>
135    *    ResettableSupplier&lt;String&gt; <jv>supplier</jv> = <jk>new</jk> ResettableSupplier&lt;&gt;(() -&gt; <js>"computed"</js>);
136    *
137    *    <jc>// Set a value directly without invoking the supplier</jc>
138    *    <jv>supplier</jv>.<jsm>set</jsm>(<js>"injected"</js>);
139    *
140    *    <jc>// get() returns the injected value</jc>
141    *    assertEquals(<js>"injected"</js>, <jv>supplier</jv>.<jsm>get</jsm>());
142    *
143    *    <jc>// Reset clears the cache, next get() will invoke the supplier</jc>
144    *    <jv>supplier</jv>.<jsm>reset</jsm>();
145    *    assertEquals(<js>"computed"</js>, <jv>supplier</jv>.<jsm>get</jsm>());
146    * </p>
147    *
148    * @param value The value to cache. Can be <jk>null</jk>.
149    */
150   public void set(T value) {
151      cache.set(opt(value));
152   }
153
154   /**
155    * Returns <jk>true</jk> if the supplier has not yet been called (cache is empty).
156    *
157    * <p>
158    * This method checks whether the cached value is <jk>null</jk>, which indicates that
159    * {@link #get()} has not yet been called, or the cache was reset.
160    *
161    * @return <jk>true</jk> if the supplier has not been called yet, <jk>false</jk> if a value has been cached.
162    */
163   public boolean isSupplied() {
164      return cache.get() == null;
165   }
166
167   /**
168    * Creates a copy of this supplier.
169    *
170    * <p>
171    * If a value has been cached, the copy will use a supplier that returns that cached value.
172    * If no value has been cached yet, the copy will use the original supplier.
173    *
174    * @return A new {@link ResettableSupplier} instance with the same state as this supplier.
175    */
176   public ResettableSupplier<T> copy() {
177      Optional<T> o = cache.get();
178      if (o == null)
179         return new ResettableSupplier<>(supplier);
180      return new ResettableSupplier<>(()->o.get());
181   }
182
183   /**
184    * If a value is present, applies the provided mapping function to it and returns a ResettableSupplier describing the result.
185    *
186    * <p>
187    * The returned ResettableSupplier maintains its own cache, independent of this supplier.
188    * Resetting the mapped supplier does not affect this supplier, and vice versa.
189    *
190    * @param <U> The type of the result of the mapping function.
191    * @param mapper A mapping function to apply to the value, if present. Must not be <jk>null</jk>.
192    * @return A ResettableSupplier describing the result of applying a mapping function to the value of this ResettableSupplier, if a value is present, otherwise an empty ResettableSupplier.
193    */
194   @Override
195   public <U> ResettableSupplier<U> map(Function<? super T, ? extends U> mapper) {
196      assertArgNotNull("mapper", mapper);
197      return new ResettableSupplier<>(() -> {
198         T value = get();
199         return nn(value) ? mapper.apply(value) : null;
200      });
201   }
202
203   /**
204    * If a value is present, and the value matches the given predicate, returns a ResettableSupplier describing the value, otherwise returns an empty ResettableSupplier.
205    *
206    * <p>
207    * The returned ResettableSupplier maintains its own cache, independent of this supplier.
208    * Resetting the filtered supplier does not affect this supplier, and vice versa.
209    *
210    * @param predicate A predicate to apply to the value, if present. Must not be <jk>null</jk>.
211    * @return A ResettableSupplier describing the value of this ResettableSupplier if a value is present and the value matches the given predicate, otherwise an empty ResettableSupplier.
212    */
213   @Override
214   public ResettableSupplier<T> filter(Predicate<? super T> predicate) {
215      assertArgNotNull("predicate", predicate);
216      return new ResettableSupplier<>(() -> {
217         T value = get();
218         return (nn(value) && predicate.test(value)) ? value : null;
219      });
220   }
221}