blob: cb7fc02e4a831237776f9cba75f032b22f384b4d [file] [log] [blame]
Aaron Kruglikov61582a02016-09-06 13:18:58 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016 Open Networking Foundation
Aaron Kruglikov61582a02016-09-06 13:18:58 -07003 *
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
Aaron Kruglikov61582a02016-09-06 13:18:58 -070019import java.util.Collection;
20import java.util.EnumSet;
21import java.util.Map;
22import java.util.Set;
23import java.util.concurrent.CompletableFuture;
Jonathan Hart46bf89b2017-02-27 15:56:42 -080024import java.util.concurrent.Executor;
Aaron Kruglikov61582a02016-09-06 13:18:58 -070025import java.util.function.BiConsumer;
26import java.util.function.BinaryOperator;
27import java.util.function.Consumer;
28import java.util.function.Function;
29import java.util.function.Supplier;
30import java.util.stream.Collector;
31import java.util.stream.Collectors;
32
Jordan Halterman21ef9e42018-05-21 22:11:07 -070033import com.google.common.collect.ImmutableMultiset;
34import com.google.common.collect.Maps;
35import com.google.common.collect.Multiset;
36import org.onlab.util.Tools;
37import org.onosproject.store.service.AsyncConsistentMultimap;
38import org.onosproject.store.service.AsyncIterator;
39import org.onosproject.store.service.MultimapEvent;
40import org.onosproject.store.service.MultimapEventListener;
41import org.onosproject.store.service.Versioned;
42
Aaron Kruglikov61582a02016-09-06 13:18:58 -070043/**
44 * An {@link AsyncConsistentMultimap} that maps its operation to operations to
45 * a differently typed {@link AsyncConsistentMultimap} by transcoding operation
46 * inputs and outputs while maintaining version numbers.
47 *
48 * @param <K2> key type of other map
49 * @param <V2> value type of other map
50 * @param <K1> key type of this map
51 * @param <V1> value type of this map
52 */
53public class TranscodingAsyncConsistentMultimap<K1, V1, K2, V2>
54 implements AsyncConsistentMultimap<K1, V1> {
55
56 private final AsyncConsistentMultimap<K2, V2> backingMap;
57 private final Function<K1, K2> keyEncoder;
58 private final Function<K2, K1> keyDecoder;
59 private final Function<V2, V1> valueDecoder;
60 private final Function<V1, V2> valueEncoder;
61 private final Function<? extends Versioned<V2>,
62 ? extends Versioned<V1>> versionedValueTransform;
63 private final Function<Versioned<Collection<? extends V2>>,
64 Versioned<Collection<? extends V1>>> versionedValueCollectionDecode;
65 private final Function<Collection<? extends V1>, Collection<V2>>
66 valueCollectionEncode;
Jonathan Hart46bf89b2017-02-27 15:56:42 -080067 private final Map<MultimapEventListener<K1, V1>, InternalBackingMultimapEventListener> listeners =
68 Maps.newIdentityHashMap();
Aaron Kruglikov61582a02016-09-06 13:18:58 -070069
70 public TranscodingAsyncConsistentMultimap(
71 AsyncConsistentMultimap<K2, V2> backingMap,
72 Function<K1, K2> keyEncoder,
73 Function<K2, K1> keyDecoder,
74 Function<V2, V1> valueDecoder,
75 Function<V1, V2> valueEncoder) {
76 this.backingMap = backingMap;
77 this.keyEncoder = k -> k == null ? null : keyEncoder.apply(k);
78 this.keyDecoder = k -> k == null ? null : keyDecoder.apply(k);
79 this.valueDecoder = v -> v == null ? null : valueDecoder.apply(v);
80 this.valueEncoder = v -> v == null ? null : valueEncoder.apply(v);
81 this.versionedValueTransform = v -> v == null ? null :
82 v.map(valueDecoder);
83 this.versionedValueCollectionDecode = v -> v == null ? null :
84 new Versioned<>(
85 v.value()
86 .stream()
87 .map(valueDecoder)
88 .collect(Collectors.toSet()),
89 v.version(),
90 v.creationTime());
91 this.valueCollectionEncode = v -> v == null ? null :
92 v.stream().map(valueEncoder).collect(Collectors.toSet());
93 }
94
95 @Override
96 public CompletableFuture<Integer> size() {
97 return backingMap.size();
98 }
99
100 @Override
101 public CompletableFuture<Boolean> isEmpty() {
102 return backingMap.isEmpty();
103 }
104
105 @Override
106 public CompletableFuture<Boolean> containsKey(K1 key) {
107 try {
108 return backingMap.containsKey(keyEncoder.apply(key));
109 } catch (Exception e) {
110 return Tools.exceptionalFuture(e);
111 }
112 }
113
114 @Override
115 public CompletableFuture<Boolean> containsValue(V1 value) {
116 try {
117 return backingMap.containsValue(valueEncoder.apply(value));
118 } catch (Exception e) {
119 return Tools.exceptionalFuture(e);
120 }
121 }
122
123 @Override
124 public CompletableFuture<Boolean> containsEntry(K1 key, V1 value) {
125 try {
126 return backingMap.containsEntry(keyEncoder.apply(key),
127 valueEncoder.apply(value));
128 } catch (Exception e) {
129 return Tools.exceptionalFuture(e);
130 }
131 }
132
133 @Override
134 public CompletableFuture<Boolean> put(K1 key, V1 value) {
135 try {
136 return backingMap.put(keyEncoder.apply(key),
137 valueEncoder.apply(value));
138 } catch (Exception e) {
139 return Tools.exceptionalFuture(e);
140 }
141 }
142
143 @Override
Jordan Halterman99c654d2018-06-04 14:53:06 -0700144 public CompletableFuture<Versioned<Collection<? extends V1>>> putAndGet(K1 key, V1 value) {
145 try {
146 return backingMap.putAndGet(keyEncoder.apply(key), valueEncoder.apply(value))
147 .thenApply(versionedValueCollectionDecode);
148 } catch (Exception e) {
149 return Tools.exceptionalFuture(e);
150 }
151 }
152
153 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700154 public CompletableFuture<Boolean> remove(K1 key, V1 value) {
155 try {
156 return backingMap.remove(keyEncoder.apply(key), valueEncoder
157 .apply(value));
158 } catch (Exception e) {
159 return Tools.exceptionalFuture(e);
160 }
161 }
162
163 @Override
Jordan Halterman99c654d2018-06-04 14:53:06 -0700164 public CompletableFuture<Versioned<Collection<? extends V1>>> removeAndGet(K1 key, V1 value) {
165 try {
166 return backingMap.removeAndGet(keyEncoder.apply(key), valueEncoder.apply(value))
167 .thenApply(versionedValueCollectionDecode);
168 } catch (Exception e) {
169 return Tools.exceptionalFuture(e);
170 }
171 }
172
173 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700174 public CompletableFuture<Boolean> removeAll(
175 K1 key, Collection<? extends V1> values) {
176 try {
177 return backingMap.removeAll(
178 keyEncoder.apply(key),
179 values.stream().map(valueEncoder).collect(
180 Collectors.toSet()));
181 } catch (Exception e) {
182 return Tools.exceptionalFuture(e);
183 }
184 }
185
186 @Override
187 public CompletableFuture<Versioned<Collection<? extends V1>>>
188 removeAll(K1 key) {
189 try {
190 return backingMap.removeAll(keyEncoder.apply(key))
191 .thenApply(versionedValueCollectionDecode);
192 } catch (Exception e) {
193 return Tools.exceptionalFuture(e);
194 }
195 }
196
197 @Override
198 public CompletableFuture<Boolean>
pierfa48c6e2019-10-11 18:19:59 +0200199 removeAll(Map<K1, Collection<? extends V1>> mapping) {
200 try {
201 // Transform the mapping to the new output
202 Map<K2, Collection<? extends V2>> transformedMapping = Maps.newHashMap();
203 mapping.forEach((key, value) -> transformedMapping.put(keyEncoder.apply(key),
204 valueCollectionEncode.apply(value)));
205 // Then apply the operation
206 return backingMap.removeAll(transformedMapping);
207 } catch (Exception e) {
208 return Tools.exceptionalFuture(e);
209 }
210 }
211
212 @Override
213 public CompletableFuture<Boolean>
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700214 putAll(K1 key, Collection<? extends V1> values) {
215 try {
216 return backingMap.putAll(keyEncoder.apply(key),
217 valueCollectionEncode.apply(values));
218 } catch (Exception e) {
219 return Tools.exceptionalFuture(e);
220 }
221 }
222
223 @Override
pierfa48c6e2019-10-11 18:19:59 +0200224 public CompletableFuture<Boolean>
225 putAll(Map<K1, Collection<? extends V1>> mapping) {
226 try {
227 // Transform the mapping to the new output
228 Map<K2, Collection<? extends V2>> transformedMapping = Maps.newHashMap();
229 mapping.forEach((key, value) -> transformedMapping.put(keyEncoder.apply(key),
230 valueCollectionEncode.apply(value)));
231 // Then apply the operation
232 return backingMap.putAll(transformedMapping);
233 } catch (Exception e) {
234 return Tools.exceptionalFuture(e);
235 }
236 }
237
238 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700239 public CompletableFuture<Versioned<Collection<? extends V1>>>
240 replaceValues(K1 key, Collection<V1> values) {
241 try {
242 return backingMap.replaceValues(keyEncoder.apply(key),
243 valueCollectionEncode.apply(values))
244 .thenApply(versionedValueCollectionDecode);
245 } catch (Exception e) {
246 return Tools.exceptionalFuture(e);
247 }
248 }
249
250 @Override
251 public CompletableFuture<Void> clear() {
252 return backingMap.clear();
253 }
254
255 @Override
256 public CompletableFuture<Versioned<Collection<? extends V1>>> get(K1 key) {
257 try {
258 return backingMap.get(keyEncoder.apply(key))
259 .thenApply(versionedValueCollectionDecode);
260 } catch (Exception e) {
261 return Tools.exceptionalFuture(e);
262 }
263 }
264
265 @Override
266 public CompletableFuture<Set<K1>> keySet() {
267 return backingMap.keySet().thenApply(s -> s.stream()
268 .map(keyDecoder)
269 .collect(Collectors.toSet()));
270 }
271
272 @Override
273 public CompletableFuture<Multiset<K1>> keys() {
274 return backingMap.keys().thenApply(s -> s.stream().map(keyDecoder)
275 .collect(new MultisetCollector<>()));
276 }
277
278 @Override
279 public CompletableFuture<Multiset<V1>> values() {
Jonathan Hartad0c3022017-02-22 14:06:01 -0800280 return backingMap.values().thenApply(s ->
281 s.stream().map(valueDecoder).collect(new MultisetCollector<>()));
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700282 }
283
284 @Override
285 public CompletableFuture<Collection<Map.Entry<K1, V1>>> entries() {
286 return backingMap.entries().thenApply(s -> s.stream()
287 .map(e -> Maps.immutableEntry(keyDecoder.apply(e.getKey()),
288 valueDecoder.apply(e.getValue())))
289 .collect(Collectors.toSet()));
290 }
291
292 @Override
Jordan Halterman21ef9e42018-05-21 22:11:07 -0700293 public CompletableFuture<AsyncIterator<Map.Entry<K1, V1>>> iterator() {
294 return backingMap.iterator().thenApply(TranscodingIterator::new);
295 }
296
297 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700298 public CompletableFuture<Map<K1, Collection<V1>>> asMap() {
299 throw new UnsupportedOperationException("Unsupported operation.");
300 }
301
302 @Override
303 public String name() {
304 return backingMap.name();
305 }
306
307 @Override
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800308 public CompletableFuture<Void> addListener(MultimapEventListener<K1, V1> listener, Executor executor) {
309 synchronized (listeners) {
310 InternalBackingMultimapEventListener backingMapListener =
311 listeners.computeIfAbsent(listener, k -> new InternalBackingMultimapEventListener(listener));
312 return backingMap.addListener(backingMapListener, executor);
313 }
314 }
315
316 @Override
317 public CompletableFuture<Void> removeListener(MultimapEventListener<K1, V1> listener) {
318 synchronized (listeners) {
319 InternalBackingMultimapEventListener backingMapListener = listeners.remove(listener);
320 if (backingMapListener != null) {
321 return backingMap.removeListener(backingMapListener);
322 } else {
323 return CompletableFuture.completedFuture(null);
324 }
325 }
326 }
327
328 @Override
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700329 public void addStatusChangeListener(Consumer<Status> listener) {
330 backingMap.addStatusChangeListener(listener);
331 }
332
333 @Override
334 public void removeStatusChangeListener(Consumer<Status> listener) {
335 backingMap.removeStatusChangeListener(listener);
336 }
337
338 @Override
339 public Collection<Consumer<Status>> statusChangeListeners() {
340 return backingMap.statusChangeListeners();
341 }
342
343 private class MultisetCollector<T> implements Collector<T,
344 ImmutableMultiset.Builder<T>,
345 Multiset<T>> {
346
347 @Override
348 public Supplier<ImmutableMultiset.Builder<T>> supplier() {
349 return ImmutableMultiset::builder;
350 }
351
352 @Override
353 public BiConsumer<ImmutableMultiset.Builder<T>, T> accumulator() {
354 return ((builder, t) -> builder.add(t));
355 }
356
357 @Override
358 public BinaryOperator<ImmutableMultiset.Builder<T>> combiner() {
359 return (a, b) -> {
360 a.addAll(b.build());
361 return a;
362 };
363 }
364
365 @Override
366 public Function<ImmutableMultiset.Builder<T>, Multiset<T>> finisher() {
367 return ImmutableMultiset.Builder::build;
368 }
369
370 @Override
371 public Set<Characteristics> characteristics() {
372 return EnumSet.of(Characteristics.UNORDERED);
373 }
374 }
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800375
Jordan Halterman21ef9e42018-05-21 22:11:07 -0700376 private class TranscodingIterator implements AsyncIterator<Map.Entry<K1, V1>> {
377 private final AsyncIterator<Map.Entry<K2, V2>> iterator;
378
379 public TranscodingIterator(AsyncIterator<Map.Entry<K2, V2>> iterator) {
380 this.iterator = iterator;
381 }
382
383 @Override
384 public CompletableFuture<Boolean> hasNext() {
385 return iterator.hasNext();
386 }
387
388 @Override
389 public CompletableFuture<Map.Entry<K1, V1>> next() {
390 return iterator.next().thenApply(entry ->
391 Maps.immutableEntry(keyDecoder.apply(entry.getKey()), valueDecoder.apply(entry.getValue())));
392 }
393 }
394
Jonathan Hart46bf89b2017-02-27 15:56:42 -0800395 private class InternalBackingMultimapEventListener implements MultimapEventListener<K2, V2> {
396
397 private final MultimapEventListener<K1, V1> listener;
398
399 InternalBackingMultimapEventListener(MultimapEventListener<K1, V1> listener) {
400 this.listener = listener;
401 }
402
403 @Override
404 public void event(MultimapEvent<K2, V2> event) {
405 listener.event(new MultimapEvent(event.name(),
406 keyDecoder.apply(event.key()),
407 valueDecoder.apply(event.newValue()),
408 valueDecoder.apply(event.oldValue())));
409 }
410 }
Aaron Kruglikov61582a02016-09-06 13:18:58 -0700411}