// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// @Internal
// public class CompletableFutureKit {
// 
//     public static <V> CompletableFuture<V> failedFuture(Exception e) {
//         CompletableFuture<V> future = new CompletableFuture<>();
//         future.completeExceptionally(e);
//         return future;
//     }
// 
//     public static <V> Throwable cause(CompletableFuture<V> completableFuture) {
//         if (!completableFuture.isCompletedExceptionally()) {
//             return null;
//         }
//         try {
//             completableFuture.get();
//             return null;
//         } catch (InterruptedException e) {
//             Thread.currentThread().interrupt();
//             return e;
//         } catch (ExecutionException e) {
//             Throwable cause = e.getCause();
//             if (cause != null) {
//                 return cause;
//             }
//             return e;
//         }
//     }
// 
//     public static <V> boolean succeeded(CompletableFuture<V> future) {
//         return future.isDone() && !future.isCompletedExceptionally();
//     }
// 
//     public static <V> boolean failed(CompletableFuture<V> future) {
//         return future.isDone() && future.isCompletedExceptionally();
//     }
// 
//     public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> cfs) {
//         return CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0]))
//                 .thenApply(v -> cfs.stream()
//                         .map(CompletableFuture::join)
//                         .collect(toList())
//                 );
//     }
// }
// 
// Path: src/main/java/org/dataloader/impl/NoOpValueCache.java
// @Internal
// public class NoOpValueCache<K, V> implements ValueCache<K, V> {
// 
//     /**
//      * a no op value cache instance
//      */
//     public static final NoOpValueCache<?, ?> NOOP = new NoOpValueCache<>();
// 
//     // avoid object allocation by using a final field
//     private final ValueCachingNotSupported NOT_SUPPORTED = new ValueCachingNotSupported();
//     private final CompletableFuture<V> NOT_SUPPORTED_CF = CompletableFutureKit.failedFuture(NOT_SUPPORTED);
//     private final CompletableFuture<Void> NOT_SUPPORTED_VOID_CF = CompletableFuture.completedFuture(null);
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<V> get(K key) {
//         return NOT_SUPPORTED_CF;
//     }
// 
//     @Override
//     public CompletableFuture<List<Try<V>>> getValues(List<K> keys) throws ValueCachingNotSupported {
//         throw NOT_SUPPORTED;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<V> set(K key, V value) {
//         return NOT_SUPPORTED_CF;
//     }
// 
//     @Override
//     public CompletableFuture<List<V>> setValues(List<K> keys, List<V> values) throws ValueCachingNotSupported {
//         throw NOT_SUPPORTED;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<Void> delete(K key) {
//         return NOT_SUPPORTED_VOID_CF;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<Void> clear() {
//         return NOT_SUPPORTED_VOID_CF;
//     }
// }


import org.dataloader.annotations.PublicSpi;
import org.dataloader.impl.CompletableFutureKit;
import org.dataloader.impl.NoOpValueCache;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
package org.dataloader;



/**
 * The {@link ValueCache} is used by data loaders that use caching and want a long-lived or external cache
 * of values.  The {@link ValueCache} is used as a place to cache values when they come back from an async
 * cache store.
 * <p>
 * It differs from {@link CacheMap} which is in fact a cache of promised values aka {@link CompletableFuture}&lt;V&gt;'s.
 * <p>
 * {@link ValueCache} is more suited to be a wrapper of a long-lived or externallly cached values.  {@link CompletableFuture}s cant
 * be easily placed in an external cache outside the JVM say, hence the need for the {@link ValueCache}.
 * <p>
 * {@link DataLoader}s use a two stage cache strategy if caching is enabled.  If the {@link CacheMap} already has the promise to a value
 * that is used.  If not then the {@link ValueCache} is asked for a value, if it has one then that is returned (and cached as a promise in the {@link CacheMap}.
 * <p>
 * If there is no value then the key is queued and loaded via the {@link BatchLoader} calls.  The returned values will then be stored in
 * the {@link ValueCache} and the promises to those values are also stored in the {@link CacheMap}.
 * <p>
 * The default implementation is a no-op store which replies with the key always missing and doesn't
 * store any actual results. This is to avoid duplicating the stored data between the {@link CacheMap}
 * out of the box.
 * <p>
 * The API signature uses {@link CompletableFuture}s because the backing implementation MAY be a remote external cache
 * and hence exceptions may happen in retrieving values and they may take time to complete.
 *
 * @param <K> the type of cache keys
 * @param <V> the type of cache values
 *
 * @author <a href="https://github.com/craig-day">Craig Day</a>
 * @author <a href="https://github.com/bbakerman/">Brad Baker</a>
 */
@PublicSpi
public interface ValueCache<K, V> {

    /**
     * Creates a new value cache, using the default no-op implementation.
     *
     * @param <K> the type of cache keys
     * @param <V> the type of cache values
     *
     * @return the cache store
     */
    static <K, V> ValueCache<K, V> defaultValueCache() {
        //noinspection unchecked
// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// @Internal
// public class CompletableFutureKit {
// 
//     public static <V> CompletableFuture<V> failedFuture(Exception e) {
//         CompletableFuture<V> future = new CompletableFuture<>();
//         future.completeExceptionally(e);
//         return future;
//     }
// 
//     public static <V> Throwable cause(CompletableFuture<V> completableFuture) {
//         if (!completableFuture.isCompletedExceptionally()) {
//             return null;
//         }
//         try {
//             completableFuture.get();
//             return null;
//         } catch (InterruptedException e) {
//             Thread.currentThread().interrupt();
//             return e;
//         } catch (ExecutionException e) {
//             Throwable cause = e.getCause();
//             if (cause != null) {
//                 return cause;
//             }
//             return e;
//         }
//     }
// 
//     public static <V> boolean succeeded(CompletableFuture<V> future) {
//         return future.isDone() && !future.isCompletedExceptionally();
//     }
// 
//     public static <V> boolean failed(CompletableFuture<V> future) {
//         return future.isDone() && future.isCompletedExceptionally();
//     }
// 
//     public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> cfs) {
//         return CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0]))
//                 .thenApply(v -> cfs.stream()
//                         .map(CompletableFuture::join)
//                         .collect(toList())
//                 );
//     }
// }
// 
// Path: src/main/java/org/dataloader/impl/NoOpValueCache.java
// @Internal
// public class NoOpValueCache<K, V> implements ValueCache<K, V> {
// 
//     /**
//      * a no op value cache instance
//      */
//     public static final NoOpValueCache<?, ?> NOOP = new NoOpValueCache<>();
// 
//     // avoid object allocation by using a final field
//     private final ValueCachingNotSupported NOT_SUPPORTED = new ValueCachingNotSupported();
//     private final CompletableFuture<V> NOT_SUPPORTED_CF = CompletableFutureKit.failedFuture(NOT_SUPPORTED);
//     private final CompletableFuture<Void> NOT_SUPPORTED_VOID_CF = CompletableFuture.completedFuture(null);
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<V> get(K key) {
//         return NOT_SUPPORTED_CF;
//     }
// 
//     @Override
//     public CompletableFuture<List<Try<V>>> getValues(List<K> keys) throws ValueCachingNotSupported {
//         throw NOT_SUPPORTED;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<V> set(K key, V value) {
//         return NOT_SUPPORTED_CF;
//     }
// 
//     @Override
//     public CompletableFuture<List<V>> setValues(List<K> keys, List<V> values) throws ValueCachingNotSupported {
//         throw NOT_SUPPORTED;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<Void> delete(K key) {
//         return NOT_SUPPORTED_VOID_CF;
//     }
// 
//     /**
//      * {@inheritDoc}
//      */
//     @Override
//     public CompletableFuture<Void> clear() {
//         return NOT_SUPPORTED_VOID_CF;
//     }
// }


// Path: src/main/java/org/dataloader/ValueCache.java
import org.dataloader.annotations.PublicSpi;
import org.dataloader.impl.CompletableFutureKit;
import org.dataloader.impl.NoOpValueCache;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

package org.dataloader;



/**
 * The {@link ValueCache} is used by data loaders that use caching and want a long-lived or external cache
 * of values.  The {@link ValueCache} is used as a place to cache values when they come back from an async
 * cache store.
 * <p>
 * It differs from {@link CacheMap} which is in fact a cache of promised values aka {@link CompletableFuture}&lt;V&gt;'s.
 * <p>
 * {@link ValueCache} is more suited to be a wrapper of a long-lived or externallly cached values.  {@link CompletableFuture}s cant
 * be easily placed in an external cache outside the JVM say, hence the need for the {@link ValueCache}.
 * <p>
 * {@link DataLoader}s use a two stage cache strategy if caching is enabled.  If the {@link CacheMap} already has the promise to a value
 * that is used.  If not then the {@link ValueCache} is asked for a value, if it has one then that is returned (and cached as a promise in the {@link CacheMap}.
 * <p>
 * If there is no value then the key is queued and loaded via the {@link BatchLoader} calls.  The returned values will then be stored in
 * the {@link ValueCache} and the promises to those values are also stored in the {@link CacheMap}.
 * <p>
 * The default implementation is a no-op store which replies with the key always missing and doesn't
 * store any actual results. This is to avoid duplicating the stored data between the {@link CacheMap}
 * out of the box.
 * <p>
 * The API signature uses {@link CompletableFuture}s because the backing implementation MAY be a remote external cache
 * and hence exceptions may happen in retrieving values and they may take time to complete.
 *
 * @param <K> the type of cache keys
 * @param <V> the type of cache values
 *
 * @author <a href="https://github.com/craig-day">Craig Day</a>
 * @author <a href="https://github.com/bbakerman/">Brad Baker</a>
 */
@PublicSpi
public interface ValueCache<K, V> {

    /**
     * Creates a new value cache, using the default no-op implementation.
     *
     * @param <K> the type of cache keys
     * @param <V> the type of cache values
     *
     * @return the cache store
     */
    static <K, V> ValueCache<K, V> defaultValueCache() {
        //noinspection unchecked
        return (ValueCache<K, V>) NoOpValueCache.NOOP;