/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.reactive;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.reactivestreams.Publisher;
import org.redisson.RedissonMap;
import org.redisson.api.RMapReactive;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.MapScanCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
import org.redisson.command.CommandReactiveExecutor;
import reactor.fn.BiFunction;
import reactor.fn.Function;
import reactor.rx.Streams;
/**
* Distributed and concurrent implementation of {@link java.util.concurrent.ConcurrentMap}
* and {@link java.util.Map}
*
* @author Nikita Koksharov
*
* @param <K> key
* @param <V> value
*/
public class RedissonMapReactive<K, V> extends RedissonExpirableReactive implements RMapReactive<K, V>, MapReactive<K, V> {
private final RedissonMap<K, V> instance;
public RedissonMapReactive(CommandReactiveExecutor commandExecutor, String name) {
super(commandExecutor, name);
instance = new RedissonMap<K, V>(null, codec, commandExecutor, name, null);
}
public RedissonMapReactive(Codec codec, CommandReactiveExecutor commandExecutor, String name) {
super(codec, commandExecutor, name);
instance = new RedissonMap<K, V>(null, codec, commandExecutor, name, null);
}
@Override
public Publisher<Integer> size() {
return reactive(instance.sizeAsync());
}
@Override
public Publisher<Boolean> containsKey(Object key) {
return reactive(instance.containsKeyAsync(key));
}
@Override
public Publisher<Boolean> containsValue(Object value) {
return reactive(instance.containsValueAsync(value));
}
@Override
public Publisher<Map<K, V>> getAll(Set<K> keys) {
return reactive(instance.getAllAsync(keys));
}
@Override
public Publisher<Void> putAll(Map<? extends K, ? extends V> map) {
return reactive(instance.putAllAsync(map));
}
@Override
public Publisher<V> putIfAbsent(K key, V value) {
return reactive(instance.putIfAbsentAsync(key, value));
}
@Override
public Publisher<Boolean> remove(Object key, Object value) {
return reactive(instance.removeAsync(key, value));
}
@Override
public Publisher<Boolean> replace(K key, V oldValue, V newValue) {
return reactive(instance.replaceAsync(key, oldValue, newValue));
}
@Override
public Publisher<V> replace(K key, V value) {
return reactive(instance.replaceAsync(key, value));
}
@Override
public Publisher<V> get(K key) {
return reactive(instance.getAsync(key));
}
@Override
public Publisher<V> put(K key, V value) {
return reactive(instance.putAsync(key, value));
}
@Override
public Publisher<V> remove(K key) {
return reactive(instance.removeAsync(key));
}
@Override
public Publisher<Boolean> fastPut(K key, V value) {
return reactive(instance.fastPutAsync(key, value));
}
@Override
public Publisher<Long> fastRemove(K ... keys) {
return reactive(instance.fastRemoveAsync(keys));
}
public Publisher<MapScanResult<ScanObjectEntry, ScanObjectEntry>> scanIteratorReactive(InetSocketAddress client, long startPos) {
return commandExecutor.readReactive(client, getName(), new MapScanCodec(codec), RedisCommands.HSCAN, getName(), startPos);
}
@Override
public Publisher<Map.Entry<K, V>> entryIterator() {
return new RedissonMapReactiveIterator<K, V, Map.Entry<K, V>>(this).stream();
}
@Override
public Publisher<V> valueIterator() {
return new RedissonMapReactiveIterator<K, V, V>(this) {
@Override
V getValue(Entry<ScanObjectEntry, ScanObjectEntry> entry) {
return (V) entry.getValue().getObj();
}
}.stream();
}
@Override
public Publisher<K> keyIterator() {
return new RedissonMapReactiveIterator<K, V, K>(this) {
@Override
K getValue(Entry<ScanObjectEntry, ScanObjectEntry> entry) {
return (K) entry.getKey().getObj();
}
}.stream();
}
@Override
public Publisher<V> addAndGet(K key, Number value) {
return reactive(instance.addAndGetAsync(key, value));
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map) {
final Map<?,?> m = (Map<?,?>) o;
if (m.size() != Streams.create(size()).next().poll()) {
return false;
}
return Streams.create(entryIterator()).map(mapFunction(m)).reduce(true, booleanAnd()).next().poll();
} else if (o instanceof RMapReactive) {
final RMapReactive<Object, Object> m = (RMapReactive<Object, Object>) o;
if (Streams.create(m.size()).next().poll() != Streams.create(size()).next().poll()) {
return false;
}
return Streams.create(entryIterator()).map(mapFunction(m)).reduce(true, booleanAnd()).next().poll();
}
return true;
}
private BiFunction<Boolean, Boolean, Boolean> booleanAnd() {
return new BiFunction<Boolean, Boolean, Boolean>() {
@Override
public Boolean apply(Boolean t, Boolean u) {
return t & u;
}
};
}
private Function<Entry<K, V>, Boolean> mapFunction(final Map<?, ?> m) {
return new Function<Map.Entry<K, V>, Boolean>() {
@Override
public Boolean apply(Entry<K, V> e) {
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
return true;
}
};
}
private Function<Entry<K, V>, Boolean> mapFunction(final RMapReactive<Object, Object> m) {
return new Function<Map.Entry<K, V>, Boolean>() {
@Override
public Boolean apply(Entry<K, V> e) {
Object key = e.getKey();
Object value = e.getValue();
if (value == null) {
if (!(Streams.create(m.get(key)).next().poll() ==null && Streams.create(m.containsKey(key)).next().poll()))
return false;
} else {
if (!value.equals(Streams.create(m.get(key)).next().poll()))
return false;
}
return true;
}
};
}
@Override
public int hashCode() {
return Streams.create(entryIterator()).map(new Function<Map.Entry<K, V>, Integer>() {
@Override
public Integer apply(Entry<K, V> t) {
return t.hashCode();
}
}).reduce(0, new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer t, Integer u) {
return t + u;
}
}).next().poll();
}
}