blob: 9d699d9a5e0237c2374a3242a9a88277a7efb504 [file] [log] [blame]
Aaron Kruglikov61582a02016-09-06 13:18:58 -07001/*
2 * Copyright 2016 Open Networking Laboratory
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
17package org.onosproject.store.primitives.impl;
18
19import com.google.common.collect.ImmutableMultiset;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Multiset;
22import org.onlab.util.Tools;
23import org.onosproject.store.service.AsyncConsistentMultimap;
Jonathan Hart46bf89b2017-02-27 15:56:42 -080024import org.onosproject.store.service.MultimapEvent;
25import org.onosproject.store.service.MultimapEventListener;
Aaron Kruglikov61582a02016-09-06 13:18:58 -070026import org.onosproject.store.service.Versioned;
27
28import java.util.Collection;
29import java.util.EnumSet;
30import java.util.Map;
31import java.util.Set;
32import java.util.concurrent.CompletableFuture;
Jonathan Hart46bf89b2017-02-27 15:56:42 -080033import java.util.concurrent.Executor;
Aaron Kruglikov61582a02016-09-06 13:18:58 -070034import java.util.function.BiConsumer;
35import java.util.function.BinaryOperator;
36import java.util.function.Consumer;
37import java.util.function.Function;
38import java.util.function.Supplier;
39import java.util.stream.Collector;
40import java.util.stream.Collectors;
41
42/**
43 * An {@link AsyncConsistentMultimap} that maps its operation to operations to
44 * a differently typed {@link AsyncConsistentMultimap} by transcoding operation
45 * inputs and outputs while maintaining version numbers.
46 *
47 * @param <K2> key type of other map
48 * @param <V2> value type of other map
49 * @param <K1> key type of this map
50 * @param <V1> value type of this map
51 */
52public class TranscodingAsyncConsistentMultimap<K1, V1, K2, V2>
53 implements AsyncConsistentMultimap<K1, V1> {
54
55 private final AsyncConsistentMultimap<K2, V2> backingMap;
56 private final Function<K1, K2> keyEncoder;
57 private final Function<K2, K1> keyDecoder;
58 private final Function<V2, V1> valueDecoder;
59 private final Function<V1, V2> valueEncoder;
60 private final Function<? extends Versioned<V2>,
61 ? extends Versioned<V1>> versionedValueTransform;
62 private final Function<Versioned<Collection<? extends V2>>,
63 Versioned<Collection<? extends V1>>> versionedValueCollectionDecode;
64 private final Function<Collection<? extends V1>, Collection<V2>>
65 valueCollectionEncode;
Jonathan Hart46bf89b2017-02-27 15:56:42 -080066 private final Map<MultimapEventListener<K1, V1>, InternalBackingMultimapEventListener> listeners =
67 Maps.newIdentityHashMap();
Aaron Kruglikov61582a02016-09-06 13:18:58 -070068
69 public TranscodingAsyncConsistentMultimap(
70 AsyncConsistentMultimap<K2, V2> backingMap,
71 Function<K1, K2> keyEncoder,
72 Function<K2, K1> keyDecoder,
73 Function<V2, V1> valueDecoder,
74 Function<V1, V2> valueEncoder) {
75 this.backingMap = backingMap;
76 this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
77 this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
78 this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
79 this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
80 this.versionedValueTransform = v -> v == null ? null :
81 v.map(valueDecoder);
82 this.versionedValueCollectionDecode = v -> v == null ? null :
83 new Versioned<>(
84 v.value()
85 .stream()
86 .map(valueDecoder)
87 .collect(Collectors.toSet()),
88 v.version(),
89 v.creationTime());
90 this.valueCollectionEncode = v -> v == null ? null :
91 v.stream().map(valueEncoder).collect(Collectors.toSet());
92 }
93
94 @Override
95 public CompletableFuture<Integer> size() {
96 return backingMap.size();
97 }
98
99 @Override
100 public CompletableFuture<Boolean> isEmpty() {
101 return backingMap.isEmpty();
102 }
103
104 @Override
105 public CompletableFuture<Boolean> containsKey(K1 key) {
106 try {
107 return backingMap.containsKey(keyEncoder.apply(key));
108 } catch (Exception e) {
109 return Tools.exceptionalFuture(e);
110 }
111 }
112
113 @Override
114 public CompletableFuture<Boolean> containsValue(V1 value) {
115 try {
116 return backingMap.containsValue(valueEncoder.apply(value));
117 } catch (Exception e) {
118 return Tools.exceptionalFuture(e);
119 }
120 }
121
122 @Override
123 public CompletableFuture<Boolean> containsEntry(K1 key, V1 value) {
124 try {
125 return backingMap.containsEntry(keyEncoder.apply(key),
126 valueEncoder.apply(value));
127 } catch (Exception e) {
128 return Tools.exceptionalFuture(e);
129 }
130 }
131
132 @Override
133 public CompletableFuture<Boolean> put(K1 key, V1 value) {
134 try {
135 return backingMap.put(keyEncoder.apply(key),
136 valueEncoder.apply(value));
137 } catch (Exception e) {
138 return Tools.exceptionalFuture(e);
139 }
140 }
141
142 @Override
143 public CompletableFuture<Boolean> remove(K1 key, V1 value) {
144 try {
145 return backingMap.remove(keyEncoder.apply(key), valueEncoder
146 .apply(value));
147 } catch (Exception e) {
148 return Tools.exceptionalFuture(e);
149 }
150 }
151
152 @Override
153 public CompletableFuture<Boolean> removeAll(
154 K1 key, Collection<? extends V1> values) {
155 try {
156 return backingMap.removeAll(
157 keyEncoder.apply(key),
158 values.stream().map(valueEncoder).collect(
159 Collectors.toSet()));
160 } catch (Exception e) {
161 return Tools.exceptionalFuture(e);
162 }
163 }
164
165 @Override
166 public CompletableFuture<Versioned<Collection<? extends V1>>>
167 removeAll(K1 key) {
168 try {
169 return backingMap.removeAll(keyEncoder.apply(key))
170 .thenApply(versionedValueCollectionDecode);
171 } catch (Exception e) {
172 return Tools.exceptionalFuture(e);
173 }
174 }
175
176 @Override
177 public CompletableFuture<Boolean>
178 putAll(K1 key, Collection<? extends V1> values) {
179 try {
180 return backingMap.putAll(keyEncoder.apply(key),
181 valueCollectionEncode.apply(values));
182 } catch (Exception e) {
183 return Tools.exceptionalFuture(e);
184 }
185 }
186
187 @Override
188 public CompletableFuture<Versioned<Collection<? extends V1>>>
189 replaceValues(K1 key, Collection<V1> values) {
190 try {
191 return backingMap.replaceValues(keyEncoder.apply(key),
192 valueCollectionEncode.apply(values))
193 .thenApply(versionedValueCollectionDecode);
194 } catch (Exception e) {
195 return Tools.exceptionalFuture(e);
196 }
197 }
198
199 @Override
200 public CompletableFuture<Void> clear() {
201 return backingMap.clear();
202 }
203
204 @Override
205 public CompletableFuture<Versioned<Collection<? extends V1>>> get(K1 key) {
206 try {
207 return backingMap.get(keyEncoder.apply(key))
208 .thenApply(versionedValueCollectionDecode);
209 } catch (Exception e) {
210 return Tools.exceptionalFuture(e);
211 }
212 }
213
214 @Override
215 public CompletableFuture<Set<K1>> keySet() {
216 return backingMap.keySet().thenApply(s -> s.stream()
217 .map(keyDecoder)
218 .collect(Collectors.toSet()));
219 }
220
221 @Override
222 public CompletableFuture<Multiset<K1>> keys() {
223 return backingMap.keys().thenApply(s -> s.stream().map(keyDecoder)
224 .collect(new MultisetCollector<>()));
225 }
226
227 @Override
228 public CompletableFuture<Multiset<V1>> values() {
Jonathan Hartad0c3022017-02-22 14:06:01 -0800229 return backingMap.values().thenApply(s ->
230 s.stream().map(valueDecoder).collect(new MultisetCollector<>()));
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700231 }
232
233 @Override
234 public CompletableFuture<Collection<Map.Entry<K1, V1>>> entries() {
235 return backingMap.entries().thenApply(s -> s.stream()
236 .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
237 valueDecoder.apply(e.getValue())))
238 .collect(Collectors.toSet()));
239 }
240
241 @Override
242 public CompletableFuture<Map<K1, Collection<V1>>> asMap() {
243 throw new UnsupportedOperationException("Unsupported operation.");
244 }
245
246 @Override
247 public String name() {
248 return backingMap.name();
249 }
250
251 @Override
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800252 public CompletableFuture<Void> addListener(MultimapEventListener<K1, V1> listener, Executor executor) {
253 synchronized (listeners) {
254 InternalBackingMultimapEventListener backingMapListener =
255 listeners.computeIfAbsent(listener, k -> new InternalBackingMultimapEventListener(listener));
256 return backingMap.addListener(backingMapListener, executor);
257 }
258 }
259
260 @Override
261 public CompletableFuture<Void> removeListener(MultimapEventListener<K1, V1> listener) {
262 synchronized (listeners) {
263 InternalBackingMultimapEventListener backingMapListener = listeners.remove(listener);
264 if (backingMapListener != null) {
265 return backingMap.removeListener(backingMapListener);
266 } else {
267 return CompletableFuture.completedFuture(null);
268 }
269 }
270 }
271
272 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700273 public void addStatusChangeListener(Consumer<Status> listener) {
274 backingMap.addStatusChangeListener(listener);
275 }
276
277 @Override
278 public void removeStatusChangeListener(Consumer<Status> listener) {
279 backingMap.removeStatusChangeListener(listener);
280 }
281
282 @Override
283 public Collection<Consumer<Status>> statusChangeListeners() {
284 return backingMap.statusChangeListeners();
285 }
286
287 private class MultisetCollector<T> implements Collector<T,
288 ImmutableMultiset.Builder<T>,
289 Multiset<T>> {
290
291 @Override
292 public Supplier<ImmutableMultiset.Builder<T>> supplier() {
293 return ImmutableMultiset::builder;
294 }
295
296 @Override
297 public BiConsumer<ImmutableMultiset.Builder<T>, T> accumulator() {
298 return ((builder, t) -> builder.add(t));
299 }
300
301 @Override
302 public BinaryOperator<ImmutableMultiset.Builder<T>> combiner() {
303 return (a, b) -> {
304 a.addAll(b.build());
305 return a;
306 };
307 }
308
309 @Override
310 public Function<ImmutableMultiset.Builder<T>, Multiset<T>> finisher() {
311 return ImmutableMultiset.Builder::build;
312 }
313
314 @Override
315 public Set<Characteristics> characteristics() {
316 return EnumSet.of(Characteristics.UNORDERED);
317 }
318 }
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800319
320 private class InternalBackingMultimapEventListener implements MultimapEventListener<K2, V2> {
321
322 private final MultimapEventListener<K1, V1> listener;
323
324 InternalBackingMultimapEventListener(MultimapEventListener<K1, V1> listener) {
325 this.listener = listener;
326 }
327
328 @Override
329 public void event(MultimapEvent<K2, V2> event) {
330 listener.event(new MultimapEvent(event.name(),
331 keyDecoder.apply(event.key()),
332 valueDecoder.apply(event.newValue()),
333 valueDecoder.apply(event.oldValue())));
334 }
335 }
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700336}