1 package com.atlassian.vcache.internal.core.service;
2
3 import com.atlassian.vcache.internal.core.RecursionDetector;
4
5 import java.util.Map;
6 import java.util.Objects;
7 import java.util.Optional;
8 import java.util.Set;
9 import java.util.function.Consumer;
10 import java.util.function.Function;
11 import java.util.stream.Collectors;
12 import java.util.stream.StreamSupport;
13
14 import static java.util.Objects.requireNonNull;
15
16
17
18
19
20 public class LocalCacheUtils {
21
22
23
24
25
26 public static class PutArgs<K, V> {
27 public final K key;
28 public final V value;
29
30 private PutArgs(K key, V value) {
31 this.key = requireNonNull(key);
32 this.value = requireNonNull(value);
33 }
34 }
35
36
37
38
39
40
41
42
43
44
45
46
47 public static <K, V> Map<K, V> getBulk(
48 Function<Set<K>, Map<K, V>> factory,
49 Iterable<K> keys,
50 Function<K, Optional<V>> getFn,
51 Consumer<PutArgs<K, V>> putFn,
52 RecursionDetector<K> recursionDetector) {
53
54 final Map<K, Optional<V>> existingValues = StreamSupport.stream(keys.spliterator(), false)
55 .distinct()
56 .collect(Collectors.toMap(
57 Objects::requireNonNull,
58 getFn));
59
60
61 @SuppressWarnings("OptionalGetWithoutIsPresent")
62 final Map<K, V> grandResult = existingValues.entrySet().stream()
63 .filter(e -> e.getValue().isPresent())
64 .collect(Collectors.toMap(
65 Map.Entry::getKey,
66 e -> e.getValue().get()));
67
68
69 if (grandResult.size() == existingValues.size()) {
70 return grandResult;
71 }
72
73
74 final Set<K> missingKeys = existingValues.entrySet().stream()
75 .filter(e -> !e.getValue().isPresent())
76 .map(Map.Entry::getKey)
77 .collect(Collectors.toSet());
78
79 try (RecursionDetector.Guard<K> ignored = recursionDetector.guardOn(missingKeys)) {
80 final Map<K, V> missingValues = factory.apply(missingKeys);
81
82 missingValues.entrySet().forEach(e -> {
83 putFn.accept(new PutArgs<>(e.getKey(), e.getValue()));
84 grandResult.put(e.getKey(), e.getValue());
85 });
86 }
87
88 return grandResult;
89 }
90 }