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

ben-manes / caffeine / #4893

08 Jun 2025 05:21AM UTC coverage: 99.948%. Remained the same
#4893

push

github

ben-manes
remove unreachable code

8 of 9 new or added lines in 2 files covered. (88.89%)

3 existing lines in 1 file now uncovered.

7733 of 7737 relevant lines covered (99.95%)

1.0 hits per line

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

99.39
/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.function.BiConsumer;
41
import java.util.function.BiFunction;
42
import java.util.function.Consumer;
43
import java.util.function.Function;
44
import java.util.function.Predicate;
45

46
import org.jspecify.annotations.Nullable;
47

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

195
  @SuppressWarnings({"FutureReturnValueIgnored", "SuspiciousMethodCalls"})
196
  default void handleCompletion(K key, CompletableFuture<? extends V> valueFuture,
197
      long startTime, boolean recordMiss) {
198
    valueFuture.whenComplete((value, error) -> {
1✔
199
      long loadTime = cache().statsTicker().read() - startTime;
1✔
200
      if (value == null) {
1✔
201
        if ((error != null) && !(error instanceof CancellationException)
1✔
202
            && !(error instanceof TimeoutException)) {
203
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", error);
1✔
204
        }
205
        cache().statsCounter().recordLoadFailure(loadTime);
1✔
206
        cache().remove(key, valueFuture);
1✔
207
      } else {
208
        @SuppressWarnings("unchecked")
209
        var castedFuture = (CompletableFuture<V>) valueFuture;
1✔
210

211
        try {
212
          // update the weight and expiration timestamps
213
          cache().replace(key, castedFuture, castedFuture, /* shouldDiscardRefresh= */ false);
1✔
214
          cache().statsCounter().recordLoadSuccess(loadTime);
1✔
215
        } catch (Throwable t) {
1✔
216
          logger.log(Level.WARNING, "Exception thrown during asynchronous load", t);
1✔
217
          cache().statsCounter().recordLoadFailure(loadTime);
1✔
218
          cache().remove(key, valueFuture);
1✔
219
        }
1✔
220
      }
221
      if (recordMiss) {
1✔
222
        cache().statsCounter().recordMisses(1);
1✔
223
      }
224
    });
1✔
225
  }
1✔
226

227
  /** A function executed asynchronously after a bulk load completes. */
228
  final class AsyncBulkCompleter<K, V> implements BiFunction<
229
      Map<? extends K, ? extends V>, Throwable, Map<? extends K, ? extends V>> {
230
    private final LocalCache<K, CompletableFuture<V>> cache;
231
    @SuppressWarnings("ImmutableMemberCollection")
232
    private final Map<K, CompletableFuture<V>> proxies;
233
    private final long startTime;
234

235
    AsyncBulkCompleter(LocalCache<K, CompletableFuture<V>> cache,
236
        Map<K, CompletableFuture<V>> proxies) {
1✔
237
      this.startTime = cache.statsTicker().read();
1✔
238
      this.proxies = proxies;
1✔
239
      this.cache = cache;
1✔
240
    }
1✔
241

242
    @Override
243
    @CanIgnoreReturnValue
244
    public @Nullable Map<? extends K, ? extends V> apply(
245
        @Nullable Map<? extends K, ? extends V> result, @Nullable Throwable error) {
246
      long loadTime = cache.statsTicker().read() - startTime;
1✔
247
      var failure = handleResponse(result, error);
1✔
248

249
      if (failure == null) {
1✔
250
        cache.statsCounter().recordLoadSuccess(loadTime);
1✔
251
        return result;
1✔
252
      }
253

254
      cache.statsCounter().recordLoadFailure(loadTime);
1✔
255
      if (failure instanceof RuntimeException) {
1✔
256
        throw (RuntimeException) failure;
1✔
UNCOV
257
      } else if (failure instanceof Error) {
×
UNCOV
258
        throw (Error) failure;
×
259
      }
UNCOV
260
      throw new CompletionException(failure);
×
261
    }
262

263
    @CanIgnoreReturnValue
264
    public CompletionException error(Throwable error) {
265
      var failure = handleResponse(/* result= */ null, error);
1✔
266
      cache.statsCounter().recordLoadFailure(0L);
1✔
267
      if (failure instanceof RuntimeException) {
1✔
268
        throw (RuntimeException) failure;
1✔
269
      } else if (failure instanceof Error) {
1✔
270
        throw (Error) failure;
1✔
271
      }
272
      throw new CompletionException(failure);
1✔
273
    }
274

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

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

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

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

344
    static final class NullMapCompletionException extends CompletionException {
1✔
345
      private static final long serialVersionUID = 1L;
346
    }
347
  }
348

349
  /* --------------- Asynchronous view --------------- */
350
  final class AsyncAsMapView<K, V> implements ConcurrentMap<K, CompletableFuture<V>> {
351
    final LocalAsyncCache<K, V> asyncCache;
352

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

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

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

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

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

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

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

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

518
  /* --------------- Synchronous view --------------- */
519
  final class CacheView<K, V> extends AbstractCacheView<K, V> {
520
    private static final long serialVersionUID = 1L;
521

522
    @SuppressWarnings("serial")
523
    final LocalAsyncCache<K, V> asyncCache;
524

525
    CacheView(LocalAsyncCache<K, V> asyncCache) {
1✔
526
      this.asyncCache = requireNonNull(asyncCache);
1✔
527
    }
1✔
528
    @Override LocalAsyncCache<K, V> asyncCache() {
529
      return asyncCache;
1✔
530
    }
531
  }
532

533
  abstract class AbstractCacheView<K, V> implements Cache<K, V>, Serializable {
1✔
534
    private static final long serialVersionUID = 1L;
535

536
    transient @Nullable ConcurrentMap<K, V> asMapView;
537

538
    abstract LocalAsyncCache<K, V> asyncCache();
539

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

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

553
      int uniqueKeys = result.size();
1✔
554
      for (var iter = result.entrySet().iterator(); iter.hasNext();) {
1✔
555
        Map.Entry<K, V> entry = iter.next();
1✔
556

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

568
      return Collections.unmodifiableMap(result);
1✔
569
    }
570

571
    @Override
572
    public V get(K key, Function<? super K, ? extends V> mappingFunction) {
573
      return resolve(asyncCache().get(key, mappingFunction));
1✔
574
    }
575

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

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

602
    @Override
603
    public void put(K key, V value) {
604
      requireNonNull(value);
1✔
605
      asyncCache().cache().put(key, CompletableFuture.completedFuture(value));
1✔
606
    }
1✔
607

608
    @Override
609
    public void putAll(Map<? extends K, ? extends V> map) {
610
      map.forEach(this::put);
1✔
611
    }
1✔
612

613
    @Override
614
    public void invalidate(K key) {
615
      asyncCache().cache().remove(key);
1✔
616
    }
1✔
617

618
    @Override
619
    public void invalidateAll(Iterable<? extends K> keys) {
620
      asyncCache().cache().invalidateAll(keys);
1✔
621
    }
1✔
622

623
    @Override
624
    public void invalidateAll() {
625
      asyncCache().cache().clear();
1✔
626
    }
1✔
627

628
    @Override
629
    public long estimatedSize() {
630
      return asyncCache().cache().estimatedSize();
1✔
631
    }
632

633
    @Override
634
    public CacheStats stats() {
635
      return asyncCache().cache().statsCounter().snapshot();
1✔
636
    }
637

638
    @Override
639
    public void cleanUp() {
640
      asyncCache().cache().cleanUp();
1✔
641
    }
1✔
642

643
    @Override
644
    public Policy<K, V> policy() {
645
      return asyncCache().policy();
1✔
646
    }
647

648
    @Override
649
    public ConcurrentMap<K, V> asMap() {
650
      return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView;
1✔
651
    }
652
  }
653

654
  final class AsMapView<K, V> implements ConcurrentMap<K, V> {
655
    final LocalCache<K, CompletableFuture<V>> delegate;
656

657
    @Nullable Set<K> keys;
658
    @Nullable Collection<V> values;
659
    @Nullable Set<Entry<K, V>> entries;
660

661
    AsMapView(LocalCache<K, CompletableFuture<V>> delegate) {
1✔
662
      this.delegate = delegate;
1✔
663
    }
1✔
664

665
    @Override
666
    public boolean isEmpty() {
667
      return delegate.isEmpty();
1✔
668
    }
669

670
    @Override
671
    public int size() {
672
      return delegate.size();
1✔
673
    }
674

675
    @Override
676
    public void clear() {
677
      delegate.clear();
1✔
678
    }
1✔
679

680
    @Override
681
    public boolean containsKey(Object key) {
682
      return Async.isReady(delegate.getIfPresentQuietly(key));
1✔
683
    }
684

685
    @Override
686
    public boolean containsValue(Object value) {
687
      requireNonNull(value);
1✔
688

689
      for (CompletableFuture<V> valueFuture : delegate.values()) {
1✔
690
        if (value.equals(Async.getIfReady(valueFuture))) {
1✔
691
          return true;
1✔
692
        }
693
      }
1✔
694
      return false;
1✔
695
    }
696

697
    @Override
698
    public @Nullable V get(Object key) {
699
      return Async.getIfReady(delegate.get(key));
1✔
700
    }
701

702
    @Override
703
    public @Nullable V putIfAbsent(K key, V value) {
704
      requireNonNull(value);
1✔
705

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

718
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
719
          if (prior != null) {
1✔
720
            return prior;
1✔
721
          }
722
        }
723

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

731
        if (added[0]) {
1✔
732
          return null;
1✔
733
        } else {
734
          V prior = Async.getWhenSuccessful(computed);
1✔
735
          if (prior != null) {
1✔
736
            return prior;
1✔
737
          }
738
        }
739
      }
1✔
740
    }
741

742
    @Override
743
    public void putAll(Map<? extends K, ? extends V> map) {
744
      map.forEach(this::put);
1✔
745
    }
1✔
746

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

755
    @Override
756
    public @Nullable V remove(Object key) {
757
      CompletableFuture<V> oldValueFuture = delegate.remove(key);
1✔
758
      return Async.getWhenSuccessful(oldValueFuture);
1✔
759
    }
760

761
    @Override
762
    public boolean remove(Object key, Object value) {
763
      requireNonNull(key);
1✔
764
      if (value == null) {
1✔
765
        return false;
1✔
766
      }
767

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

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

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

796
        if (done[0]) {
1✔
797
          return removed[0];
1✔
798
        }
799
      }
800
    }
801

802
    @Override
803
    public @Nullable V replace(K key, V value) {
804
      requireNonNull(value);
1✔
805

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

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

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

829
        if (done[0]) {
1✔
830
          return oldValue[0];
1✔
831
        }
832
      }
1✔
833
    }
834

835
    @Override
836
    public boolean replace(K key, V oldValue, V newValue) {
837
      requireNonNull(oldValue);
1✔
838
      requireNonNull(newValue);
1✔
839

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

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

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

862
        if (done[0]) {
1✔
863
          return replaced[0];
1✔
864
        }
865
      }
1✔
866
    }
867

868
    @Override
869
    public @Nullable V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
870
      requireNonNull(mappingFunction);
1✔
871

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

883
          V prior = Async.getWhenSuccessful(priorFuture);
1✔
884
          if (prior != null) {
1✔
885
            delegate.statsCounter().recordHits(1);
1✔
886
            return prior;
1✔
887
          }
888
        }
889

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

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

906
        V result = Async.getWhenSuccessful(computed);
1✔
907
        if ((computed == future[0]) || (result != null)) {
1✔
908
          return result;
1✔
909
        }
910
      }
1✔
911
    }
912

913
    @Override
914
    public @Nullable V computeIfPresent(K key,
915
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
916
      requireNonNull(remappingFunction);
1✔
917

918
      @SuppressWarnings({"rawtypes", "unchecked"})
919
      var newValue = (V[]) new Object[1];
1✔
920
      for (;;) {
921
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
922

923
        CompletableFuture<V> valueFuture = delegate.computeIfPresent(key, (k, oldValueFuture) -> {
1✔
924
          if (!oldValueFuture.isDone()) {
1✔
925
            return oldValueFuture;
1✔
926
          }
927

928
          V oldValue = Async.getIfReady(oldValueFuture);
1✔
929
          if (oldValue == null) {
1✔
930
            return null;
1✔
931
          }
932

933
          newValue[0] = remappingFunction.apply(key, oldValue);
1✔
934
          return (newValue[0] == null) ? null : CompletableFuture.completedFuture(newValue[0]);
1✔
935
        });
936

937
        if (newValue[0] != null) {
1✔
938
          return newValue[0];
1✔
939
        } else if (valueFuture == null) {
1✔
940
          return null;
1✔
941
        }
942
      }
1✔
943
    }
944

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

951
      @SuppressWarnings({"rawtypes", "unchecked"})
952
      var newValue = (V[]) new Object[1];
1✔
953
      for (;;) {
954
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
955

956
        CompletableFuture<V> valueFuture = delegate.compute(key, (k, oldValueFuture) -> {
1✔
957
          if ((oldValueFuture != null) && !oldValueFuture.isDone()) {
1✔
958
            return oldValueFuture;
1✔
959
          }
960

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

968
        if (newValue[0] != null) {
1✔
969
          return newValue[0];
1✔
970
        } else if (valueFuture == null) {
1✔
971
          return null;
1✔
972
        }
973
      }
1✔
974
    }
975

976
    @Override
977
    public @Nullable V merge(K key, V value,
978
        BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
979
      requireNonNull(value);
1✔
980
      requireNonNull(remappingFunction);
1✔
981

982
      CompletableFuture<V> newValueFuture = CompletableFuture.completedFuture(value);
1✔
983
      boolean[] merged = { false };
1✔
984
      for (;;) {
985
        Async.getWhenSuccessful(delegate.getIfPresentQuietly(key));
1✔
986

987
        CompletableFuture<V> mergedValueFuture = delegate.merge(
1✔
988
            key, newValueFuture, (oldValueFuture, valueFuture) -> {
989
          if ((oldValueFuture != null) && !oldValueFuture.isDone()) {
1✔
990
            return oldValueFuture;
1✔
991
          }
992

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

1009
        if (merged[0] || (mergedValueFuture == newValueFuture)) {
1✔
1010
          return Async.getWhenSuccessful(mergedValueFuture);
1✔
1011
        }
1012
      }
1✔
1013
    }
1014

1015
    @Override
1016
    public Set<K> keySet() {
1017
      return (keys == null) ? (keys = new KeySet()) : keys;
1✔
1018
    }
1019

1020
    @Override
1021
    public Collection<V> values() {
1022
      return (values == null) ? (values = new Values()) : values;
1✔
1023
    }
1024

1025
    @Override
1026
    public Set<Entry<K, V>> entrySet() {
1027
      return (entries == null) ? (entries = new EntrySet()) : entries;
1✔
1028
    }
1029

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

1039
      var map = (Map<?, ?>) o;
1✔
1040
      int expectedSize = size();
1✔
1041
      if (map.size() != expectedSize) {
1✔
1042
        return false;
1✔
1043
      }
1044

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

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

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

1076
        if (iterator.hasNext()) {
1✔
1077
          result.append(", ");
1✔
1078
        }
1079
      }
1✔
1080
      return result.append('}').toString();
1✔
1081
    }
1082

1083
    private final class KeySet extends AbstractSet<K> {
1✔
1084

1085
      @Override
1086
      public boolean isEmpty() {
1087
        return AsMapView.this.isEmpty();
1✔
1088
      }
1089

1090
      @Override
1091
      public int size() {
1092
        return AsMapView.this.size();
1✔
1093
      }
1094

1095
      @Override
1096
      public void clear() {
1097
        AsMapView.this.clear();
1✔
1098
      }
1✔
1099

1100
      @Override
1101
      public boolean contains(Object o) {
1102
        return AsMapView.this.containsKey(o);
1✔
1103
      }
1104

1105
      @Override
1106
      public boolean removeAll(Collection<?> collection) {
1107
        return delegate.keySet().removeAll(collection);
1✔
1108
      }
1109

1110
      @Override
1111
      public boolean remove(Object o) {
1112
        return delegate.keySet().remove(o);
1✔
1113
      }
1114

1115
      @Override
1116
      public boolean removeIf(Predicate<? super K> filter) {
1117
        return delegate.keySet().removeIf(filter);
1✔
1118
      }
1119

1120
      @Override
1121
      public boolean retainAll(Collection<?> collection) {
1122
        return delegate.keySet().retainAll(collection);
1✔
1123
      }
1124

1125
      @Override
1126
      public Iterator<K> iterator() {
1127
        return new Iterator<>() {
1✔
1128
          final Iterator<Entry<K, V>> iterator = entrySet().iterator();
1✔
1129

1130
          @Override
1131
          public boolean hasNext() {
1132
            return iterator.hasNext();
1✔
1133
          }
1134

1135
          @Override
1136
          public K next() {
1137
            return iterator.next().getKey();
1✔
1138
          }
1139

1140
          @Override
1141
          public void remove() {
1142
            iterator.remove();
1✔
1143
          }
1✔
1144
        };
1145
      }
1146
    }
1147

1148
    private final class Values extends AbstractCollection<V> {
1✔
1149

1150
      @Override
1151
      public boolean isEmpty() {
1152
        return AsMapView.this.isEmpty();
1✔
1153
      }
1154

1155
      @Override
1156
      public int size() {
1157
        return AsMapView.this.size();
1✔
1158
      }
1159

1160
      @Override
1161
      public void clear() {
1162
        AsMapView.this.clear();
1✔
1163
      }
1✔
1164

1165
      @Override
1166
      public boolean contains(Object o) {
1167
        return AsMapView.this.containsValue(o);
1✔
1168
      }
1169

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

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

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

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

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

1232
      @Override
1233
      public Iterator<V> iterator() {
1234
        return new Iterator<>() {
1✔
1235
          final Iterator<Entry<K, V>> iterator = entrySet().iterator();
1✔
1236

1237
          @Override
1238
          public boolean hasNext() {
1239
            return iterator.hasNext();
1✔
1240
          }
1241

1242
          @Override
1243
          public V next() {
1244
            return iterator.next().getValue();
1✔
1245
          }
1246

1247
          @Override
1248
          public void remove() {
1249
            iterator.remove();
1✔
1250
          }
1✔
1251
        };
1252
      }
1253
    }
1254

1255
    private final class EntrySet extends AbstractSet<Entry<K, V>> {
1✔
1256

1257
      @Override
1258
      public boolean isEmpty() {
1259
        return AsMapView.this.isEmpty();
1✔
1260
      }
1261

1262
      @Override
1263
      public int size() {
1264
        return AsMapView.this.size();
1✔
1265
      }
1266

1267
      @Override
1268
      public void clear() {
1269
        AsMapView.this.clear();
1✔
1270
      }
1✔
1271

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

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

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

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

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

1339
      @Override
1340
      public Iterator<Entry<K, V>> iterator() {
1341
        return new EntryIterator();
1✔
1342
      }
1343
    }
1344

1345
    private final class EntryIterator implements Iterator<Entry<K, V>> {
1346
      final Iterator<Entry<K, CompletableFuture<V>>> iterator;
1347
      @Nullable Entry<K, V> cursor;
1348
      @Nullable K removalKey;
1349

1350
      EntryIterator() {
1✔
1351
        iterator = delegate.entrySet().iterator();
1✔
1352
      }
1✔
1353

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

1366
      @Override
1367
      public Entry<K, V> next() {
1368
        if (!hasNext()) {
1✔
1369
          throw new NoSuchElementException();
1✔
1370
        }
1371
        K key = requireNonNull(cursor).getKey();
1✔
1372
        Entry<K, V> entry = cursor;
1✔
1373
        removalKey = key;
1✔
1374
        cursor = null;
1✔
1375
        return entry;
1✔
1376
      }
1377

1378
      @Override
1379
      public void remove() {
1380
        requireState(removalKey != null);
1✔
1381
        delegate.remove(removalKey);
1✔
1382
        removalKey = null;
1✔
1383
      }
1✔
1384
    }
1385
  }
1386
}
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