• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #4774

08 Apr 2025 03:54AM UTC coverage: 99.057% (+0.01%) from 99.044%
#4774

push

github

ben-manes
resolve build properties lazily during configuration time

7671 of 7744 relevant lines covered (99.06%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.54
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalAsyncCache.java
1
/*
2
 * Copyright 2018 Ben Manes. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.github.benmanes.caffeine.cache;
17

18
import static com.github.benmanes.caffeine.cache.Caffeine.calculateHashMapCapacity;
19
import static com.github.benmanes.caffeine.cache.Caffeine.requireState;
20
import static java.util.Objects.requireNonNull;
21

22
import java.io.Serializable;
23
import java.lang.System.Logger;
24
import java.lang.System.Logger.Level;
25
import java.util.AbstractCollection;
26
import java.util.AbstractSet;
27
import java.util.Collection;
28
import java.util.Collections;
29
import java.util.Iterator;
30
import java.util.LinkedHashMap;
31
import java.util.Map;
32
import java.util.NoSuchElementException;
33
import java.util.Set;
34
import java.util.concurrent.CancellationException;
35
import java.util.concurrent.CompletableFuture;
36
import java.util.concurrent.CompletionException;
37
import java.util.concurrent.ConcurrentMap;
38
import java.util.concurrent.Executor;
39
import java.util.concurrent.TimeoutException;
40
import java.util.concurrent.atomic.AtomicBoolean;
41
import java.util.function.BiConsumer;
42
import java.util.function.BiFunction;
43
import java.util.function.Consumer;
44
import java.util.function.Function;
45
import java.util.function.Predicate;
46

47
import org.jspecify.annotations.Nullable;
48

49
import com.github.benmanes.caffeine.cache.LocalAsyncCache.AsyncBulkCompleter.NullMapCompletionException;
50
import com.github.benmanes.caffeine.cache.stats.CacheStats;
51
import com.google.errorprone.annotations.CanIgnoreReturnValue;
52
import com.google.errorprone.annotations.Var;
53

54
/**
55
 * This class provides a skeletal implementation of the {@link AsyncCache} interface to minimize the
56
 * effort required to implement a {@link LocalCache}.
57
 *
58
 * @author ben.manes@gmail.com (Ben Manes)
59
 */
60
interface LocalAsyncCache<K, V> extends AsyncCache<K, V> {
61
  Logger logger = System.getLogger(LocalAsyncCache.class.getName());
1✔
62

63
  /** Returns the backing {@link LocalCache} data store. */
64
  LocalCache<K, CompletableFuture<V>> cache();
65

66
  /** Returns the policy supported by this implementation and its configuration. */
67
  Policy<K, V> policy();
68

69
  @Override
70
  default @Nullable CompletableFuture<V> getIfPresent(K key) {
71
    return cache().getIfPresent(key, /* recordStats= */ true);
1✔
72
  }
73

74
  @Override
75
  default CompletableFuture<V> get(K key, Function<? super K, ? extends V> mappingFunction) {
76
    requireNonNull(mappingFunction);
1✔
77
    return get(key, (k1, executor) -> CompletableFuture.supplyAsync(
1✔
78
        () -> mappingFunction.apply(key), executor));
1✔
79
  }
80

81
  @Override
82
  default CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor,
83
      ? extends CompletableFuture<? extends V>> mappingFunction) {
84
    return get(key, mappingFunction, /* recordStats= */ true);
1✔
85
  }
86

87
  @SuppressWarnings({"FutureReturnValueIgnored", "NullAway"})
88
  default CompletableFuture<V> get(K key, BiFunction<? super K, ? super Executor,
89
      ? extends CompletableFuture<? extends V>> mappingFunction, boolean recordStats) {
90
    long startTime = cache().statsTicker().read();
1✔
91
    @SuppressWarnings({"rawtypes", "unchecked"})
92
    CompletableFuture<? extends V>[] result = new CompletableFuture[1];
1✔
93
    CompletableFuture<V> future = cache().computeIfAbsent(key, k -> {
1✔
94
      @SuppressWarnings("unchecked")
95
      var castedResult = (CompletableFuture<V>) mappingFunction.apply(key, cache().executor());
1✔
96
      result[0] = castedResult;
1✔
97
      return requireNonNull(castedResult);
1✔
98
    }, recordStats, /* recordLoad= */ false);
99
    if (result[0] != null) {
1✔
100
      handleCompletion(key, result[0], startTime, /* recordMiss= */ false);
1✔
101
    }
102
    return future;
1✔
103
  }
104

105
  @Override
106
  default CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys,
107
      Function<? super Set<? extends K>, ? extends Map<? extends K, ? extends V>> mappingFunction) {
108
    requireNonNull(mappingFunction);
1✔
109
    return getAll(keys, (keysToLoad, executor) ->
1✔
110
        CompletableFuture.supplyAsync(() -> mappingFunction.apply(keysToLoad), executor));
1✔
111
  }
112

113
  @Override
114
  @SuppressWarnings("FutureReturnValueIgnored")
115
  default CompletableFuture<Map<K, V>> getAll(Iterable<? extends K> keys,
116
      BiFunction<? super Set<? extends K>, ? super Executor,
117
          ? extends CompletableFuture<? extends Map<? extends K, ? extends V>>> mappingFunction) {
118
    requireNonNull(mappingFunction);
1✔
119
    requireNonNull(keys);
1✔
120

121
    int initialCapacity = calculateHashMapCapacity(keys);
1✔
122
    var futures = new LinkedHashMap<K, CompletableFuture<V>>(initialCapacity);
1✔
123
    var proxies = new LinkedHashMap<K, CompletableFuture<V>>(initialCapacity);
1✔
124
    for (K key : keys) {
1✔
125
      if (futures.containsKey(key)) {
1✔
126
        continue;
1✔
127
      }
128
      @Var CompletableFuture<V> future = cache().getIfPresent(key, /* recordStats= */ false);
1✔
129
      if (future == null) {
1✔
130
        var proxy = new CompletableFuture<V>();
1✔
131
        future = cache().putIfAbsent(key, proxy);
1✔
132
        if (future == null) {
1✔
133
          future = proxy;
1✔
134
          proxies.put(key, proxy);
1✔
135
        }
136
      }
137
      futures.put(key, future);
1✔
138
    }
1✔
139
    cache().statsCounter().recordMisses(proxies.size());
1✔
140
    cache().statsCounter().recordHits(futures.size() - proxies.size());
1✔
141
    if (proxies.isEmpty()) {
1✔
142
      return composeResult(futures);
1✔
143
    }
144

145
    var completer = new AsyncBulkCompleter<>(cache(), proxies);
1✔
146
    try {
147
      var loader = mappingFunction.apply(
1✔
148
          Collections.unmodifiableSet(proxies.keySet()), cache().executor());
1✔
149
      return loader.handle(completer).thenCompose(ignored -> composeResult(futures));
1✔
150
    } catch (Throwable t) {
1✔
151
      completer.apply(/* result= */ null, t);
×
152
      throw t;
×
153
    }
154
  }
155

156
  /**
157
   * Returns a future that waits for all of the dependent futures to complete and returns the
158
   * combined mapping if successful. If any future fails then it is automatically removed from
159
   * the cache if still present.
160
   */
161
  static <K, V> CompletableFuture<Map<K, V>> composeResult(Map<K, CompletableFuture<V>> futures) {
162
    if (futures.isEmpty()) {
1✔
163
      @SuppressWarnings("ImmutableMapOf")
164
      Map<K, V> emptyMap = Collections.unmodifiableMap(Collections.emptyMap());
1✔
165
      return CompletableFuture.completedFuture(emptyMap);
1✔
166
    }
167
    @SuppressWarnings("rawtypes")
168
    CompletableFuture<?>[] array = futures.values().toArray(new CompletableFuture[0]);
1✔
169
    return CompletableFuture.allOf(array).thenApply(ignored -> {
1✔
170
      var result = new LinkedHashMap<K, V>(calculateHashMapCapacity(futures.size()));
1✔
171
      futures.forEach((key, future) -> {
1✔
172
        V value = future.getNow(null);
1✔
173
        if (value != null) {
1✔
174
          result.put(key, value);
1✔
175
        }
176
      });
1✔
177
      return Collections.unmodifiableMap(result);
1✔
178
    });
179
  }
180

181
  @Override
182
  @SuppressWarnings("FutureReturnValueIgnored")
183
  default void put(K key, CompletableFuture<? extends V> valueFuture) {
184
    if (valueFuture.isCompletedExceptionally()
1✔
185
        || (valueFuture.isDone() && (valueFuture.join() == null))) {
1✔
186
      cache().statsCounter().recordLoadFailure(0L);
1✔
187
      cache().remove(key);
1✔
188
      return;
1✔
189
    }
190
    long startTime = cache().statsTicker().read();
1✔
191

192
    @SuppressWarnings("unchecked")
193
    var castedFuture = (CompletableFuture<V>) valueFuture;
1✔
194
    cache().put(key, castedFuture);
1✔
195
    handleCompletion(key, valueFuture, startTime, /* recordMiss= */ false);
1✔
196
  }
1✔
197

198
  @SuppressWarnings({"FutureReturnValueIgnored", "SuspiciousMethodCalls"})
199
  default void handleCompletion(K key, CompletableFuture<? extends V> valueFuture,
200
      long startTime, boolean recordMiss) {
201
    var completed = new AtomicBoolean();
1✔
202
    valueFuture.whenComplete((value, error) -> {
1✔
203
      if (!completed.compareAndSet(/* expectedValue= */ false, /* newValue= */ true)) {
1✔
204
        // Ignore multiple invocations due to ForkJoinPool retrying on delays
205
        return;
×
206
      }
207
      long loadTime = cache().statsTicker().read() - startTime;
1✔
208
      if (value == null) {
1✔
209
        if ((error != null) && !(error instanceof CancellationException)
1✔
210
            && !(error instanceof TimeoutException)) {
211
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", error);
1✔
212
        }
213
        cache().statsCounter().recordLoadFailure(loadTime);
1✔
214
        cache().remove(key, valueFuture);
1✔
215
      } else {
216
        @SuppressWarnings("unchecked")
217
        var castedFuture = (CompletableFuture<V>) valueFuture;
1✔
218

219
        try {
220
          // update the weight and expiration timestamps
221
          cache().replace(key, castedFuture, castedFuture, /* shouldDiscardRefresh= */ false);
1✔
222
          cache().statsCounter().recordLoadSuccess(loadTime);
1✔
223
        } catch (Throwable t) {
1✔
224
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
225
          cache().statsCounter().recordLoadFailure(loadTime);
1✔
226
          cache().remove(key, valueFuture);
1✔
227
        }
1✔
228
      }
229
      if (recordMiss) {
1✔
230
        cache().statsCounter().recordMisses(1);
1✔
231
      }
232
    });
1✔
233
  }
1✔
234

235
  /** A function executed asynchronously after a bulk load completes. */
236
  final class AsyncBulkCompleter<K, V> implements BiFunction<
237
      Map<? extends K, ? extends V>, Throwable, Map<? extends K, ? extends V>> {
238
    private final LocalCache<K, CompletableFuture<V>> cache;
239
    @SuppressWarnings("ImmutableMemberCollection")
240
    private final Map<K, CompletableFuture<V>> proxies;
241
    private final long startTime;
242

243
    AsyncBulkCompleter(LocalCache<K, CompletableFuture<V>> cache,
244
        Map<K, CompletableFuture<V>> proxies) {
1✔
245
      this.startTime = cache.statsTicker().read();
1✔
246
      this.proxies = proxies;
1✔
247
      this.cache = cache;
1✔
248
    }
1✔
249

250
    @Override
251
    @CanIgnoreReturnValue
252
    public @Nullable Map<? extends K, ? extends V> apply(
253
        @Nullable Map<? extends K, ? extends V> result, @Nullable Throwable error) {
254
      long loadTime = cache.statsTicker().read() - startTime;
1✔
255
      var failure = handleResponse(result, error);
1✔
256

257
      if (failure == null) {
1✔
258
        cache.statsCounter().recordLoadSuccess(loadTime);
1✔
259
        return result;
1✔
260
      }
261

262
      cache.statsCounter().recordLoadFailure(loadTime);
1✔
263
      if (failure instanceof RuntimeException) {
1✔
264
        throw (RuntimeException) failure;
1✔
265
      } else if (failure instanceof Error) {
1✔
266
        throw (Error) failure;
1✔
267
      }
268
      throw new CompletionException(failure);
1✔
269
    }
270

271
    private @Nullable Throwable handleResponse(
272
        @Nullable Map<? extends K, ? extends V> result, @Nullable Throwable error) {
273
      if (result == null) {
1✔
274
        var failure = (error == null) ? new NullMapCompletionException() : error;
1✔
275
        for (var entry : proxies.entrySet()) {
1✔
276
          cache.remove(entry.getKey(), entry.getValue());
1✔
277
          entry.getValue().obtrudeException(failure);
1✔
278
        }
1✔
279
        if (!(failure instanceof CancellationException) && !(failure instanceof TimeoutException)) {
1✔
280
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", failure);
1✔
281
        }
282
        return failure;
1✔
283
      }
284
      var failure = fillProxies(result);
1✔
285
      return addNewEntries(result, failure);
1✔
286
    }
287

288
    /** Populates the proxies with the computed result. */
289
    private @Nullable Throwable fillProxies(Map<? extends K, ? extends V> result) {
290
      @Var Throwable error = null;
1✔
291
      for (var entry : proxies.entrySet()) {
1✔
292
        var key = entry.getKey();
1✔
293
        var value = result.get(key);
1✔
294
        var future = entry.getValue();
1✔
295
        future.obtrudeValue(value);
1✔
296

297
        if (value == null) {
1✔
298
          cache.remove(key, future);
1✔
299
        } else {
300
          try {
301
            // update the weight and expiration timestamps
302
            cache.replace(key, future, future);
1✔
303
          } catch (Throwable t) {
1✔
304
            logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
305
            cache.remove(key, future);
1✔
306
            if (error == null) {
1✔
307
              error = t;
1✔
308
            } else {
309
              error.addSuppressed(t);
1✔
310
            }
311
          }
1✔
312
        }
313
      }
1✔
314
      return error;
1✔
315
    }
316

317
    /** Adds to the cache any extra entries computed that were not requested. */
318
    private @Nullable Throwable addNewEntries(
319
        Map<? extends K, ? extends V> result, @Nullable Throwable failure) {
320
      @Var Throwable error = failure;
1✔
321
      for (var entry : result.entrySet()) {
1✔
322
        var key = entry.getKey();
1✔
323
        var value = result.get(key);
1✔
324
        if (!proxies.containsKey(key)) {
1✔
325
          try {
326
            cache.put(key, CompletableFuture.completedFuture(value));
1✔
327
          } catch (Throwable t) {
1✔
328
            logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
329
            if (error == null) {
1✔
330
              error = t;
1✔
331
            } else {
332
              error.addSuppressed(t);
1✔
333
            }
334
          }
1✔
335
        }
336
      }
1✔
337
      return error;
1✔
338
    }
339

340
    static final class NullMapCompletionException extends CompletionException {
1✔
341
      private static final long serialVersionUID = 1L;
342
    }
343
  }
344

345
  /* --------------- Asynchronous view --------------- */
346
  final class AsyncAsMapView<K, V> implements ConcurrentMap<K, CompletableFuture<V>> {
347
    final LocalAsyncCache<K, V> asyncCache;
348

349
    AsyncAsMapView(LocalAsyncCache<K, V> asyncCache) {
1✔
350
      this.asyncCache = requireNonNull(asyncCache);
1✔
351
    }
1✔
352
    @Override public boolean isEmpty() {
353
      return asyncCache.cache().isEmpty();
1✔
354
    }
355
    @Override public int size() {
356
      return asyncCache.cache().size();
1✔
357
    }
358
    @Override public void clear() {
359
      asyncCache.cache().clear();
1✔
360
    }
1✔
361
    @Override public boolean containsKey(Object key) {
362
      return asyncCache.cache().containsKey(key);
1✔
363
    }
364
    @SuppressWarnings("CollectionUndefinedEquality")
365
    @Override public boolean containsValue(Object value) {
366
      return asyncCache.cache().containsValue(value);
1✔
367
    }
368
    @Override public @Nullable CompletableFuture<V> get(Object key) {
369
      return asyncCache.cache().get(key);
1✔
370
    }
371
    @Override public CompletableFuture<V> putIfAbsent(K key, CompletableFuture<V> value) {
372
      CompletableFuture<V> prior = asyncCache.cache().putIfAbsent(key, value);
1✔
373
      long startTime = asyncCache.cache().statsTicker().read();
1✔
374
      if (prior == null) {
1✔
375
        asyncCache.handleCompletion(key, value, startTime, /* recordMiss= */ false);
1✔
376
      }
377
      return prior;
1✔
378
    }
379
    @Override public CompletableFuture<V> put(K key, CompletableFuture<V> value) {
380
      CompletableFuture<V> prior = asyncCache.cache().put(key, value);
1✔
381
      long startTime = asyncCache.cache().statsTicker().read();
1✔
382
      asyncCache.handleCompletion(key, value, startTime, /* recordMiss= */ false);
1✔
383
      return prior;
1✔
384
    }
385
    @SuppressWarnings("FutureReturnValueIgnored")
386
    @Override public void putAll(Map<? extends K, ? extends CompletableFuture<V>> map) {
387
      map.forEach(this::put);
1✔
388
    }
1✔
389
    @Override public CompletableFuture<V> replace(K key, CompletableFuture<V> value) {
390
      CompletableFuture<V> prior = asyncCache.cache().replace(key, value);
1✔
391
      long startTime = asyncCache.cache().statsTicker().read();
1✔
392
      if (prior != null) {
1✔
393
        asyncCache.handleCompletion(key, value, startTime, /* recordMiss= */ false);
1✔
394
      }
395
      return prior;
1✔
396
    }
397
    @Override
398
    public boolean replace(K key, CompletableFuture<V> oldValue, CompletableFuture<V> newValue) {
399
      boolean replaced = asyncCache.cache().replace(key, oldValue, newValue);
1✔
400
      long startTime = asyncCache.cache().statsTicker().read();
1✔
401
      if (replaced) {
1✔
402
        asyncCache.handleCompletion(key, newValue, startTime, /* recordMiss= */ false);
1✔
403
      }
404
      return replaced;
1✔
405
    }
406
    @Override public CompletableFuture<V> remove(Object key) {
407
      return asyncCache.cache().remove(key);
1✔
408
    }
409
    @Override public boolean remove(Object key, Object value) {
410
      return asyncCache.cache().remove(key, value);
1✔
411
    }
412
    @SuppressWarnings("FutureReturnValueIgnored")
413
    @Override public @Nullable CompletableFuture<V> computeIfAbsent(K key,
414
        Function<? super K, ? extends CompletableFuture<V>> mappingFunction) {
415
      requireNonNull(mappingFunction);
1✔
416
      @SuppressWarnings({"rawtypes", "unchecked"})
417
      CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
418
      long startTime = asyncCache.cache().statsTicker().read();
1✔
419
      CompletableFuture<V> future = asyncCache.cache().computeIfAbsent(key, k -> {
1✔
420
        result[0] = mappingFunction.apply(k);
1✔
421
        return result[0];
1✔
422
      }, /* recordStats= */ false, /* recordLoad= */ false);
423

424
      if (result[0] == null) {
1✔
425
        if ((future != null) && asyncCache.cache().isRecordingStats()) {
1✔
426
          future.whenComplete((r, e) -> {
1✔
427
            if ((r != null) || (e == null)) {
1✔
428
              asyncCache.cache().statsCounter().recordHits(1);
1✔
429
            }
430
          });
1✔
431
        }
432
      } else {
433
        asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss= */ true);
1✔
434
      }
435
      return future;
1✔
436
    }
437
    @Override public @Nullable CompletableFuture<V> computeIfPresent(K key, BiFunction<? super K,
438
        ? super CompletableFuture<V>, ? extends CompletableFuture<V>> remappingFunction) {
439
      requireNonNull(remappingFunction);
1✔
440

441
      @SuppressWarnings({"rawtypes", "unchecked"})
442
      @Nullable CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
443
      long startTime = asyncCache.cache().statsTicker().read();
1✔
444
      asyncCache.cache().compute(key, (k, oldValue) -> {
1✔
445
        result[0] = (oldValue == null) ? null : remappingFunction.apply(k, oldValue);
1✔
446
        return result[0];
1✔
447
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
448

449
      if (result[0] != null) {
1✔
450
        asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss= */ false);
1✔
451
      }
452
      return result[0];
1✔
453
    }
454
    @Override public CompletableFuture<V> compute(K key, BiFunction<? super K,
455
        ? super CompletableFuture<V>, ? extends CompletableFuture<V>> remappingFunction) {
456
      requireNonNull(remappingFunction);
1✔
457

458
      @SuppressWarnings({"rawtypes", "unchecked"})
459
      CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
460
      long startTime = asyncCache.cache().statsTicker().read();
1✔
461
      asyncCache.cache().compute(key, (k, oldValue) -> {
1✔
462
        result[0] = remappingFunction.apply(k, oldValue);
1✔
463
        return result[0];
1✔
464
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
465

466
      if (result[0] != null) {
1✔
467
        asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss= */ false);
1✔
468
      }
469
      return result[0];
1✔
470
    }
471
    @Override public CompletableFuture<V> merge(K key, CompletableFuture<V> value,
472
        BiFunction<? super CompletableFuture<V>, ? super CompletableFuture<V>,
473
            ? extends CompletableFuture<V>> remappingFunction) {
474
      requireNonNull(value);
1✔
475
      requireNonNull(remappingFunction);
1✔
476

477
      @SuppressWarnings({"rawtypes", "unchecked"})
478
      CompletableFuture<V>[] result = new CompletableFuture[1];
1✔
479
      long startTime = asyncCache.cache().statsTicker().read();
1✔
480
      asyncCache.cache().compute(key, (k, oldValue) -> {
1✔
481
        result[0] = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
1✔
482
        return result[0];
1✔
483
      }, asyncCache.cache().expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
484

485
      if (result[0] != null) {
1✔
486
        asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss= */ false);
1✔
487
      }
488
      return result[0];
1✔
489
    }
490
    @Override public void forEach(BiConsumer<? super K, ? super CompletableFuture<V>> action) {
491
      asyncCache.cache().forEach(action);
1✔
492
    }
1✔
493
    @Override public Set<K> keySet() {
494
      return asyncCache.cache().keySet();
1✔
495
    }
496
    @Override public Collection<CompletableFuture<V>> values() {
497
      return asyncCache.cache().values();
1✔
498
    }
499
    @Override public Set<Entry<K, CompletableFuture<V>>> entrySet() {
500
      return asyncCache.cache().entrySet();
1✔
501
    }
502
    @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
503
    @Override public boolean equals(@Nullable Object o) {
504
      return asyncCache.cache().equals(o);
1✔
505
    }
506
    @Override public int hashCode() {
507
      return asyncCache.cache().hashCode();
1✔
508
    }
509
    @Override public String toString() {
510
      return asyncCache.cache().toString();
1✔
511
    }
512
  }
513

514
  /* --------------- Synchronous view --------------- */
515
  final class CacheView<K, V> extends AbstractCacheView<K, V> {
516
    private static final long serialVersionUID = 1L;
517

518
    @SuppressWarnings("serial")
519
    final LocalAsyncCache<K, V> asyncCache;
520

521
    CacheView(LocalAsyncCache<K, V> asyncCache) {
1✔
522
      this.asyncCache = requireNonNull(asyncCache);
1✔
523
    }
1✔
524
    @Override LocalAsyncCache<K, V> asyncCache() {
525
      return asyncCache;
1✔
526
    }
527
  }
528

529
  abstract class AbstractCacheView<K, V> implements Cache<K, V>, Serializable {
1✔
530
    private static final long serialVersionUID = 1L;
531

532
    transient @Nullable ConcurrentMap<K, V> asMapView;
533

534
    abstract LocalAsyncCache<K, V> asyncCache();
535

536
    @Override
537
    public @Nullable V getIfPresent(K key) {
538
      CompletableFuture<V> future = asyncCache().cache().getIfPresent(key, /* recordStats= */ true);
1✔
539
      return Async.getIfReady(future);
1✔
540
    }
541

542
    @Override
543
    public Map<K, V> getAllPresent(Iterable<? extends K> keys) {
544
      var result = new LinkedHashMap<K, V>(calculateHashMapCapacity(keys));
1✔
545
      for (K key : keys) {
1✔
546
        result.put(key, null);
1✔
547
      }
1✔
548

549
      int uniqueKeys = result.size();
1✔
550
      for (var iter = result.entrySet().iterator(); iter.hasNext();) {
1✔
551
        Map.Entry<K, V> entry = iter.next();
1✔
552

553
        CompletableFuture<V> future = asyncCache().cache().get(entry.getKey());
1✔
554
        V value = Async.getIfReady(future);
1✔
555
        if (value == null) {
1✔
556
          iter.remove();
1✔
557
        } else {
558
          entry.setValue(value);
1✔
559
        }
560
      }
1✔
561
      asyncCache().cache().statsCounter().recordHits(result.size());
1✔
562
      asyncCache().cache().statsCounter().recordMisses(uniqueKeys - result.size());
1✔
563

564
      return Collections.unmodifiableMap(result);
1✔
565
    }
566

567
    @Override
568
    public V get(K key, Function<? super K, ? extends V> mappingFunction) {
569
      return resolve(asyncCache().get(key, mappingFunction));
1✔
570
    }
571

572
    @Override
573
    public Map<K, V> getAll(
574
        Iterable<? extends K> keys,
575
        Function<
576
            ? super Set<? extends K>,
577
            ? extends Map<? extends K, ? extends V>> mappingFunction) {
578
      return resolve(asyncCache().getAll(keys, mappingFunction));
1✔
579
    }
580

581
    @SuppressWarnings({"PMD.AvoidThrowingNullPointerException",
582
      "PMD.PreserveStackTrace", "UnusedException"})
583
    protected static <T> T resolve(CompletableFuture<T> future) {
584
      try {
585
        return future.join();
1✔
586
      } catch (NullMapCompletionException e) {
1✔
587
        throw new NullPointerException("null map");
1✔
588
      } catch (CompletionException e) {
1✔
589
        if (e.getCause() instanceof RuntimeException) {
1✔
590
          throw (RuntimeException) e.getCause();
1✔
591
        } else if (e.getCause() instanceof Error) {
1✔
592
          throw (Error) e.getCause();
1✔
593
        }
594
        throw e;
1✔
595
      }
596
    }
597

598
    @Override
599
    public void put(K key, V value) {
600
      requireNonNull(value);
1✔
601
      asyncCache().cache().put(key, CompletableFuture.completedFuture(value));
1✔
602
    }
1✔
603

604
    @Override
605
    public void putAll(Map<? extends K, ? extends V> map) {
606
      map.forEach(this::put);
1✔
607
    }
1✔
608

609
    @Override
610
    public void invalidate(K key) {
611
      asyncCache().cache().remove(key);
1✔
612
    }
1✔
613

614
    @Override
615
    public void invalidateAll(Iterable<? extends K> keys) {
616
      asyncCache().cache().invalidateAll(keys);
1✔
617
    }
1✔
618

619
    @Override
620
    public void invalidateAll() {
621
      asyncCache().cache().clear();
1✔
622
    }
1✔
623

624
    @Override
625
    public long estimatedSize() {
626
      return asyncCache().cache().estimatedSize();
1✔
627
    }
628

629
    @Override
630
    public CacheStats stats() {
631
      return asyncCache().cache().statsCounter().snapshot();
1✔
632
    }
633

634
    @Override
635
    public void cleanUp() {
636
      asyncCache().cache().cleanUp();
1✔
637
    }
1✔
638

639
    @Override
640
    public Policy<K, V> policy() {
641
      return asyncCache().policy();
1✔
642
    }
643

644
    @Override
645
    public ConcurrentMap<K, V> asMap() {
646
      return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView;
1✔
647
    }
648
  }
649

650
  final class AsMapView<K, V> implements ConcurrentMap<K, V> {
651
    final LocalCache<K, CompletableFuture<V>> delegate;
652

653
    @Nullable Set<K> keys;
654
    @Nullable Collection<V> values;
655
    @Nullable Set<Entry<K, V>> entries;
656

657
    AsMapView(LocalCache<K, CompletableFuture<V>> delegate) {
1✔
658
      this.delegate = delegate;
1✔
659
    }
1✔
660

661
    @Override
662
    public boolean isEmpty() {
663
      return delegate.isEmpty();
1✔
664
    }
665

666
    @Override
667
    public int size() {
668
      return delegate.size();
1✔
669
    }
670

671
    @Override
672
    public void clear() {
673
      delegate.clear();
1✔
674
    }
1✔
675

676
    @Override
677
    public boolean containsKey(Object key) {
678
      return Async.isReady(delegate.getIfPresentQuietly(key));
1✔
679
    }
680

681
    @Override
682
    public boolean containsValue(Object value) {
683
      requireNonNull(value);
1✔
684

685
      for (CompletableFuture<V> valueFuture : delegate.values()) {
1✔
686
        if (value.equals(Async.getIfReady(valueFuture))) {
1✔
687
          return true;
1✔
688
        }
689
      }
1✔
690
      return false;
1✔
691
    }
692

693
    @Override
694
    public @Nullable V get(Object key) {
695
      return Async.getIfReady(delegate.get(key));
1✔
696
    }
697

698
    @Override
699
    public @Nullable V putIfAbsent(K key, V value) {
700
      requireNonNull(value);
1✔
701

702
      // Keep in sync with BoundedVarExpiration.putIfAbsentAsync(key, value, duration, unit)
703
      @Var CompletableFuture<V> priorFuture = null;
1✔
704
      for (;;) {
705
        priorFuture = (priorFuture == null)
1✔
706
            ? delegate.get(key)
1✔
707
            : delegate.getIfPresentQuietly(key);
1✔
708
        if (priorFuture != null) {
1✔
709
          if (!priorFuture.isDone()) {
1✔
710
            Async.getWhenSuccessful(priorFuture);
1✔
711
            continue;
1✔
712
          }
713

714
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
715
          if (prior != null) {
1✔
716
            return prior;
1✔
717
          }
718
        }
719

720
        boolean[] added = { false };
1✔
721
        CompletableFuture<V> computed = delegate.compute(key, (k, valueFuture) -> {
1✔
722
          added[0] = (valueFuture == null)
1✔
723
              || (valueFuture.isDone() && (Async.getIfReady(valueFuture) == null));
1✔
724
          return added[0] ? CompletableFuture.completedFuture(value) : valueFuture;
1✔
725
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
726

727
        if (added[0]) {
1✔
728
          return null;
1✔
729
        } else {
730
          V prior = Async.getWhenSuccessful(computed);
1✔
731
          if (prior != null) {
1✔
732
            return prior;
1✔
733
          }
734
        }
735
      }
×
736
    }
737

738
    @Override
739
    public void putAll(Map<? extends K, ? extends V> map) {
740
      map.forEach(this::put);
1✔
741
    }
1✔
742

743
    @Override
744
    public @Nullable V put(K key, V value) {
745
      requireNonNull(value);
1✔
746
      CompletableFuture<V> oldValueFuture =
1✔
747
          delegate.put(key, CompletableFuture.completedFuture(value));
1✔
748
      return Async.getWhenSuccessful(oldValueFuture);
1✔
749
    }
750

751
    @Override
752
    public @Nullable V remove(Object key) {
753
      CompletableFuture<V> oldValueFuture = delegate.remove(key);
1✔
754
      return Async.getWhenSuccessful(oldValueFuture);
1✔
755
    }
756

757
    @Override
758
    public boolean remove(Object key, Object value) {
759
      requireNonNull(key);
1✔
760
      if (value == null) {
1✔
761
        return false;
1✔
762
      }
763

764
      @SuppressWarnings("unchecked")
765
      var castedKey = (K) key;
1✔
766
      boolean[] done = { false };
1✔
767
      boolean[] removed = { false };
1✔
768
      @Var CompletableFuture<V> future = null;
1✔
769
      for (;;) {
770
        future = (future == null)
1✔
771
            ? delegate.get(castedKey)
1✔
772
            : delegate.getIfPresentQuietly(castedKey);
1✔
773
        if ((future == null) || future.isCompletedExceptionally()) {
1✔
774
          return false;
1✔
775
        }
776

777
        Async.getWhenSuccessful(future);
1✔
778
        delegate.compute(castedKey, (k, oldValueFuture) -> {
1✔
779
          if (oldValueFuture == null) {
1✔
780
            done[0] = true;
1✔
781
            return null;
1✔
782
          } else if (!oldValueFuture.isDone()) {
1✔
783
            return oldValueFuture;
×
784
          }
785

786
          done[0] = true;
1✔
787
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
788
          removed[0] = value.equals(oldValue);
1✔
789
          return (oldValue == null) || removed[0] ? null : oldValueFuture;
1✔
790
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ true);
1✔
791

792
        if (done[0]) {
1✔
793
          return removed[0];
1✔
794
        }
795
      }
796
    }
797

798
    @Override
799
    public @Nullable V replace(K key, V value) {
800
      requireNonNull(value);
1✔
801

802
      @SuppressWarnings({"rawtypes", "unchecked", "Varifier"})
803
      @Nullable V[] oldValue = (V[]) new Object[1];
1✔
804
      boolean[] done = { false };
1✔
805
      for (;;) {
806
        CompletableFuture<V> future = delegate.getIfPresentQuietly(key);
1✔
807
        if ((future == null) || future.isCompletedExceptionally()) {
1✔
808
          return null;
1✔
809
        }
810

811
        Async.getWhenSuccessful(future);
1✔
812
        delegate.compute(key, (k, oldValueFuture) -> {
1✔
813
          if (oldValueFuture == null) {
1✔
814
            done[0] = true;
1✔
815
            return null;
1✔
816
          } else if (!oldValueFuture.isDone()) {
1✔
817
            return oldValueFuture;
×
818
          }
819

820
          done[0] = true;
1✔
821
          oldValue[0] = Async.getIfReady(oldValueFuture);
1✔
822
          return (oldValue[0] == null) ? null : CompletableFuture.completedFuture(value);
1✔
823
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
824

825
        if (done[0]) {
1✔
826
          return oldValue[0];
1✔
827
        }
828
      }
×
829
    }
830

831
    @Override
832
    public boolean replace(K key, V oldValue, V newValue) {
833
      requireNonNull(oldValue);
1✔
834
      requireNonNull(newValue);
1✔
835

836
      boolean[] done = { false };
1✔
837
      boolean[] replaced = { false };
1✔
838
      for (;;) {
839
        CompletableFuture<V> future = delegate.getIfPresentQuietly(key);
1✔
840
        if ((future == null) || future.isCompletedExceptionally()) {
1✔
841
          return false;
1✔
842
        }
843

844
        Async.getWhenSuccessful(future);
1✔
845
        delegate.compute(key, (k, oldValueFuture) -> {
1✔
846
          if (oldValueFuture == null) {
1✔
847
            done[0] = true;
1✔
848
            return null;
1✔
849
          } else if (!oldValueFuture.isDone()) {
1✔
850
            return oldValueFuture;
×
851
          }
852

853
          done[0] = true;
1✔
854
          replaced[0] = oldValue.equals(Async.getIfReady(oldValueFuture));
1✔
855
          return replaced[0] ? CompletableFuture.completedFuture(newValue) : oldValueFuture;
1✔
856
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
857

858
        if (done[0]) {
1✔
859
          return replaced[0];
1✔
860
        }
861
      }
×
862
    }
863

864
    @Override
865
    public @Nullable V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
866
      requireNonNull(mappingFunction);
1✔
867

868
      @Var CompletableFuture<V> priorFuture = null;
1✔
869
      for (;;) {
870
        priorFuture = (priorFuture == null)
1✔
871
            ? delegate.get(key)
1✔
872
            : delegate.getIfPresentQuietly(key);
1✔
873
        if (priorFuture != null) {
1✔
874
          if (!priorFuture.isDone()) {
1✔
875
            Async.getWhenSuccessful(priorFuture);
1✔
876
            continue;
1✔
877
          }
878

879
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
880
          if (prior != null) {
1✔
881
            delegate.statsCounter().recordHits(1);
1✔
882
            return prior;
1✔
883
          }
884
        }
885

886
        @SuppressWarnings({"rawtypes", "unchecked"})
887
        CompletableFuture<V>[] future = new CompletableFuture[1];
1✔
888
        CompletableFuture<V> computed = delegate.compute(key, (k, valueFuture) -> {
1✔
889
          if ((valueFuture != null) && valueFuture.isDone()
1✔
890
              && (Async.getIfReady(valueFuture) != null)) {
1✔
891
            return valueFuture;
1✔
892
          }
893

894
          V newValue = delegate.statsAware(mappingFunction, /* recordLoad= */ true).apply(key);
1✔
895
          if (newValue == null) {
1✔
896
            return null;
1✔
897
          }
898
          future[0] = CompletableFuture.completedFuture(newValue);
1✔
899
          return future[0];
1✔
900
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
901

902
        V result = Async.getWhenSuccessful(computed);
1✔
903
        if ((computed == future[0]) || (result != null)) {
1✔
904
          return result;
1✔
905
        }
906
      }
×
907
    }
908

909
    @Override
910
    public @Nullable V computeIfPresent(K key,
911
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
912
      requireNonNull(remappingFunction);
1✔
913

914
      @SuppressWarnings({"rawtypes", "unchecked"})
915
      var newValue = (V[]) new Object[1];
1✔
916
      for (;;) {
917
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
918

919
        CompletableFuture<V> valueFuture = delegate.computeIfPresent(key, (k, oldValueFuture) -> {
1✔
920
          if (!oldValueFuture.isDone()) {
1✔
921
            return oldValueFuture;
×
922
          }
923

924
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
925
          if (oldValue == null) {
1✔
926
            return null;
1✔
927
          }
928

929
          newValue[0] = remappingFunction.apply(key, oldValue);
1✔
930
          return (newValue[0] == null) ? null : CompletableFuture.completedFuture(newValue[0]);
1✔
931
        });
932

933
        if (newValue[0] != null) {
1✔
934
          return newValue[0];
1✔
935
        } else if (valueFuture == null) {
1✔
936
          return null;
1✔
937
        }
938
      }
×
939
    }
940

941
    @Override
942
    public @Nullable V compute(K key,
943
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
944
      // Keep in sync with BoundedVarExpiration.computeAsync(key, remappingFunction, expiry)
945
      requireNonNull(remappingFunction);
1✔
946

947
      @SuppressWarnings({"rawtypes", "unchecked"})
948
      var newValue = (V[]) new Object[1];
1✔
949
      for (;;) {
950
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
951

952
        CompletableFuture<V> valueFuture = delegate.compute(key, (k, oldValueFuture) -> {
1✔
953
          if ((oldValueFuture != null) && !oldValueFuture.isDone()) {
1✔
954
            return oldValueFuture;
×
955
          }
956

957
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
958
          BiFunction<? super K, ? super V, ? extends V> function = delegate.statsAware(
1✔
959
              remappingFunction, /* recordLoad= */ true, /* recordLoadFailure= */ true);
960
          newValue[0] = function.apply(key, oldValue);
1✔
961
          return (newValue[0] == null) ? null : CompletableFuture.completedFuture(newValue[0]);
1✔
962
        }, delegate.expiry(), /* recordLoad= */ false, /* recordLoadFailure= */ false);
1✔
963

964
        if (newValue[0] != null) {
1✔
965
          return newValue[0];
1✔
966
        } else if (valueFuture == null) {
1✔
967
          return null;
1✔
968
        }
969
      }
×
970
    }
971

972
    @Override
973
    public @Nullable V merge(K key, V value,
974
        BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
975
      requireNonNull(value);
1✔
976
      requireNonNull(remappingFunction);
1✔
977

978
      CompletableFuture<V> newValueFuture = CompletableFuture.completedFuture(value);
1✔
979
      boolean[] merged = { false };
1✔
980
      for (;;) {
981
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
982

983
        CompletableFuture<V> mergedValueFuture = delegate.merge(
1✔
984
            key, newValueFuture, (oldValueFuture, valueFuture) -> {
985
          if ((oldValueFuture != null) && !oldValueFuture.isDone()) {
1✔
986
            return oldValueFuture;
×
987
          }
988

989
          merged[0] = true;
1✔
990
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
991
          if (oldValue == null) {
1✔
992
            return valueFuture;
1✔
993
          }
994
          V mergedValue = remappingFunction.apply(oldValue, value);
1✔
995
          if (mergedValue == null) {
1✔
996
            return null;
1✔
997
          } else if (mergedValue == oldValue) {
1✔
998
            return oldValueFuture;
1✔
999
          } else if (mergedValue == value) {
1✔
1000
            return valueFuture;
1✔
1001
          }
1002
          return CompletableFuture.completedFuture(mergedValue);
1✔
1003
        });
1004

1005
        if (merged[0] || (mergedValueFuture == newValueFuture)) {
1✔
1006
          return Async.getWhenSuccessful(mergedValueFuture);
1✔
1007
        }
1008
      }
×
1009
    }
1010

1011
    @Override
1012
    public Set<K> keySet() {
1013
      return (keys == null) ? (keys = new KeySet()) : keys;
1✔
1014
    }
1015

1016
    @Override
1017
    public Collection<V> values() {
1018
      return (values == null) ? (values = new Values()) : values;
1✔
1019
    }
1020

1021
    @Override
1022
    public Set<Entry<K, V>> entrySet() {
1023
      return (entries == null) ? (entries = new EntrySet()) : entries;
1✔
1024
    }
1025

1026
    /** See {@link BoundedLocalCache#equals(Object)} for semantics. */
1027
    @Override
1028
    public boolean equals(@Nullable Object o) {
1029
      if (o == this) {
1✔
1030
        return true;
1✔
1031
      } else if (!(o instanceof Map)) {
1✔
1032
        return false;
1✔
1033
      }
1034

1035
      var map = (Map<?, ?>) o;
1✔
1036
      int expectedSize = size();
1✔
1037
      if (map.size() != expectedSize) {
1✔
1038
        return false;
1✔
1039
      }
1040

1041
      @Var int count = 0;
1✔
1042
      for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1043
        var entry = iterator.next();
1✔
1044
        var value = map.get(entry.getKey());
1✔
1045
        if ((value == null) || ((value != entry.getValue()) && !value.equals(entry.getValue()))) {
1✔
1046
          return false;
1✔
1047
        }
1048
        count++;
1✔
1049
      }
1✔
1050
      return (count == expectedSize);
1✔
1051
    }
1052

1053
    @Override
1054
    public int hashCode() {
1055
      @Var int hash = 0;
1✔
1056
      for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1057
        var entry = iterator.next();
1✔
1058
        hash += entry.hashCode();
1✔
1059
      }
1✔
1060
      return hash;
1✔
1061
    }
1062

1063
    @Override
1064
    public String toString() {
1065
      var result = new StringBuilder(50).append('{');
1✔
1066
      for (var iterator = new EntryIterator(); iterator.hasNext();) {
1✔
1067
        var entry = iterator.next();
1✔
1068
        result.append((entry.getKey() == this) ? "(this Map)" : entry.getKey())
1✔
1069
            .append('=')
1✔
1070
            .append((entry.getValue() == this) ? "(this Map)" : entry.getValue());
1✔
1071

1072
        if (iterator.hasNext()) {
1✔
1073
          result.append(", ");
1✔
1074
        }
1075
      }
1✔
1076
      return result.append('}').toString();
1✔
1077
    }
1078

1079
    private final class KeySet extends AbstractSet<K> {
1✔
1080

1081
      @Override
1082
      public boolean isEmpty() {
1083
        return AsMapView.this.isEmpty();
1✔
1084
      }
1085

1086
      @Override
1087
      public int size() {
1088
        return AsMapView.this.size();
1✔
1089
      }
1090

1091
      @Override
1092
      public void clear() {
1093
        AsMapView.this.clear();
1✔
1094
      }
1✔
1095

1096
      @Override
1097
      public boolean contains(Object o) {
1098
        return AsMapView.this.containsKey(o);
1✔
1099
      }
1100

1101
      @Override
1102
      public boolean removeAll(Collection<?> collection) {
1103
        return delegate.keySet().removeAll(collection);
1✔
1104
      }
1105

1106
      @Override
1107
      public boolean remove(Object o) {
1108
        return delegate.keySet().remove(o);
1✔
1109
      }
1110

1111
      @Override
1112
      public boolean removeIf(Predicate<? super K> filter) {
1113
        return delegate.keySet().removeIf(filter);
1✔
1114
      }
1115

1116
      @Override
1117
      public boolean retainAll(Collection<?> collection) {
1118
        return delegate.keySet().retainAll(collection);
1✔
1119
      }
1120

1121
      @Override
1122
      public Iterator<K> iterator() {
1123
        return new Iterator<>() {
1✔
1124
          final Iterator<Entry<K, V>> iterator = entrySet().iterator();
1✔
1125

1126
          @Override
1127
          public boolean hasNext() {
1128
            return iterator.hasNext();
1✔
1129
          }
1130

1131
          @Override
1132
          public K next() {
1133
            return iterator.next().getKey();
1✔
1134
          }
1135

1136
          @Override
1137
          public void remove() {
1138
            iterator.remove();
1✔
1139
          }
1✔
1140
        };
1141
      }
1142
    }
1143

1144
    private final class Values extends AbstractCollection<V> {
1✔
1145

1146
      @Override
1147
      public boolean isEmpty() {
1148
        return AsMapView.this.isEmpty();
1✔
1149
      }
1150

1151
      @Override
1152
      public int size() {
1153
        return AsMapView.this.size();
1✔
1154
      }
1155

1156
      @Override
1157
      public void clear() {
1158
        AsMapView.this.clear();
1✔
1159
      }
1✔
1160

1161
      @Override
1162
      public boolean contains(Object o) {
1163
        return AsMapView.this.containsValue(o);
1✔
1164
      }
1165

1166
      @Override
1167
      public boolean removeAll(Collection<?> collection) {
1168
        requireNonNull(collection);
1✔
1169
        @Var boolean modified = false;
1✔
1170
        for (var entry : delegate.entrySet()) {
1✔
1171
          V value = Async.getIfReady(entry.getValue());
1✔
1172
          if ((value != null) && collection.contains(value)
1✔
1173
              && AsMapView.this.remove(entry.getKey(), value)) {
1✔
1174
            modified = true;
1✔
1175
          }
1176
        }
1✔
1177
        return modified;
1✔
1178
      }
1179

1180
      @Override
1181
      public boolean remove(Object o) {
1182
        if (o == null) {
1✔
1183
          return false;
1✔
1184
        }
1185
        for (var entry : delegate.entrySet()) {
1✔
1186
          V value = Async.getIfReady(entry.getValue());
1✔
1187
          if ((value != null) && value.equals(o) && AsMapView.this.remove(entry.getKey(), value)) {
1✔
1188
            return true;
1✔
1189
          }
1190
        }
1✔
1191
        return false;
1✔
1192
      }
1193

1194
      @Override
1195
      public boolean removeIf(Predicate<? super V> filter) {
1196
        requireNonNull(filter);
1✔
1197
        return delegate.values().removeIf(future -> {
1✔
1198
          V value = Async.getIfReady(future);
1✔
1199
          return (value != null) && filter.test(value);
1✔
1200
        });
1201
      }
1202

1203
      @Override
1204
      public boolean retainAll(Collection<?> collection) {
1205
        requireNonNull(collection);
1✔
1206
        @Var boolean modified = false;
1✔
1207
        for (var entry : delegate.entrySet()) {
1✔
1208
          V value = Async.getIfReady(entry.getValue());
1✔
1209
          if ((value != null) && !collection.contains(value)
1✔
1210
              && AsMapView.this.remove(entry.getKey(), value)) {
1✔
1211
            modified = true;
1✔
1212
          }
1213
        }
1✔
1214
        return modified;
1✔
1215
      }
1216

1217
      @Override
1218
      public void forEach(Consumer<? super V> action) {
1219
        requireNonNull(action);
1✔
1220
        delegate.values().forEach(future -> {
1✔
1221
          V value = Async.getIfReady(future);
1✔
1222
          if (value != null) {
1✔
1223
            action.accept(value);
1✔
1224
          }
1225
        });
1✔
1226
      }
1✔
1227

1228
      @Override
1229
      public Iterator<V> iterator() {
1230
        return new Iterator<>() {
1✔
1231
          final Iterator<Entry<K, V>> iterator = entrySet().iterator();
1✔
1232

1233
          @Override
1234
          public boolean hasNext() {
1235
            return iterator.hasNext();
1✔
1236
          }
1237

1238
          @Override
1239
          public V next() {
1240
            return iterator.next().getValue();
1✔
1241
          }
1242

1243
          @Override
1244
          public void remove() {
1245
            iterator.remove();
1✔
1246
          }
1✔
1247
        };
1248
      }
1249
    }
1250

1251
    private final class EntrySet extends AbstractSet<Entry<K, V>> {
1✔
1252

1253
      @Override
1254
      public boolean isEmpty() {
1255
        return AsMapView.this.isEmpty();
1✔
1256
      }
1257

1258
      @Override
1259
      public int size() {
1260
        return AsMapView.this.size();
1✔
1261
      }
1262

1263
      @Override
1264
      public void clear() {
1265
        AsMapView.this.clear();
1✔
1266
      }
1✔
1267

1268
      @Override
1269
      public boolean contains(Object o) {
1270
        if (!(o instanceof Entry<?, ?>)) {
1✔
1271
          return false;
1✔
1272
        }
1273
        var entry = (Entry<?, ?>) o;
1✔
1274
        var key = entry.getKey();
1✔
1275
        var value = entry.getValue();
1✔
1276
        if ((key == null) || (value == null)) {
1✔
1277
          return false;
1✔
1278
        }
1279
        V cachedValue = AsMapView.this.get(key);
1✔
1280
        return (cachedValue != null) && cachedValue.equals(value);
1✔
1281
      }
1282

1283
      @Override
1284
      public boolean removeAll(Collection<?> collection) {
1285
        requireNonNull(collection);
1✔
1286
        @Var boolean modified = false;
1✔
1287
        if ((collection instanceof Set<?>) && (collection.size() > size())) {
1✔
1288
          for (var entry : this) {
1✔
1289
            if (collection.contains(entry)) {
1✔
1290
              modified |= remove(entry);
1✔
1291
            }
1292
          }
1✔
1293
        } else {
1294
          for (var o : collection) {
1✔
1295
            modified |= remove(o);
1✔
1296
          }
1✔
1297
        }
1298
        return modified;
1✔
1299
      }
1300

1301
      @Override
1302
      public boolean remove(Object obj) {
1303
        if (!(obj instanceof Entry<?, ?>)) {
1✔
1304
          return false;
1✔
1305
        }
1306
        var entry = (Entry<?, ?>) obj;
1✔
1307
        var key = entry.getKey();
1✔
1308
        return (key != null) && AsMapView.this.remove(key, entry.getValue());
1✔
1309
      }
1310

1311
      @Override
1312
      public boolean removeIf(Predicate<? super Entry<K, V>> filter) {
1313
        requireNonNull(filter);
1✔
1314
        @Var boolean modified = false;
1✔
1315
        for (Entry<K, V> entry : this) {
1✔
1316
          if (filter.test(entry)) {
1✔
1317
            modified |= AsMapView.this.remove(entry.getKey(), entry.getValue());
1✔
1318
          }
1319
        }
1✔
1320
        return modified;
1✔
1321
      }
1322

1323
      @Override
1324
      public boolean retainAll(Collection<?> collection) {
1325
        requireNonNull(collection);
1✔
1326
        @Var boolean modified = false;
1✔
1327
        for (var entry : this) {
1✔
1328
          if (!collection.contains(entry) && remove(entry)) {
1✔
1329
            modified = true;
1✔
1330
          }
1331
        }
1✔
1332
        return modified;
1✔
1333
      }
1334

1335
      @Override
1336
      public Iterator<Entry<K, V>> iterator() {
1337
        return new EntryIterator();
1✔
1338
      }
1339
    }
1340

1341
    private final class EntryIterator implements Iterator<Entry<K, V>> {
1342
      final Iterator<Entry<K, CompletableFuture<V>>> iterator;
1343
      @Nullable Entry<K, V> cursor;
1344
      @Nullable K removalKey;
1345

1346
      EntryIterator() {
1✔
1347
        iterator = delegate.entrySet().iterator();
1✔
1348
      }
1✔
1349

1350
      @Override
1351
      public boolean hasNext() {
1352
        while ((cursor == null) && iterator.hasNext()) {
1✔
1353
          Entry<K, CompletableFuture<V>> entry = iterator.next();
1✔
1354
          V value = Async.getIfReady(entry.getValue());
1✔
1355
          if (value != null) {
1✔
1356
            cursor = new WriteThroughEntry<>(AsMapView.this, entry.getKey(), value);
1✔
1357
          }
1358
        }
1✔
1359
        return (cursor != null);
1✔
1360
      }
1361

1362
      @Override
1363
      public Entry<K, V> next() {
1364
        if (!hasNext()) {
1✔
1365
          throw new NoSuchElementException();
1✔
1366
        }
1367
        @SuppressWarnings("NullAway")
1368
        K key = cursor.getKey();
1✔
1369
        Entry<K, V> entry = cursor;
1✔
1370
        removalKey = key;
1✔
1371
        cursor = null;
1✔
1372
        return entry;
1✔
1373
      }
1374

1375
      @Override
1376
      public void remove() {
1377
        requireState(removalKey != null);
1✔
1378
        delegate.remove(removalKey);
1✔
1379
        removalKey = null;
1✔
1380
      }
1✔
1381
    }
1382
  }
1383
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc