/*
* Copyright (c) 2011-2014 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.core.shareddata.impl;
import io.vertx.core.shareddata.LocalMap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import static io.vertx.core.shareddata.impl.Checker.checkType;
import static io.vertx.core.shareddata.impl.Checker.copyIfRequired;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
class LocalMapImpl<K, V> implements LocalMap<K, V> {
private final ConcurrentMap<String, LocalMap<?, ?>> maps;
private final String name;
private final ConcurrentMap<K, V> map = new ConcurrentHashMap<>();
LocalMapImpl(String name, ConcurrentMap<String, LocalMap<?, ?>> maps) {
this.name = name;
this.maps = maps;
}
@Override
public V get(Object key) {
return copyIfRequired(map.get(key));
}
@Override
public V put(K key, V value) {
checkType(key);
checkType(value);
return map.put(key, value);
}
@Override
public V remove(Object key) {
return copyIfRequired(map.remove(key));
}
@Override
public void clear() {
map.clear();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public V putIfAbsent(K key, V value) {
checkType(key);
checkType(value);
return copyIfRequired(map.putIfAbsent(key, value));
}
@Override
public boolean remove(Object key, Object value) {
return map.remove(key, value);
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return map.replace(key, oldValue, newValue);
}
@Override
public boolean removeIfPresent(K key, V value) {
return map.remove(key, value);
}
@Override
public boolean replaceIfPresent(K key, V oldValue, V newValue) {
checkType(key);
checkType(oldValue);
checkType(newValue);
return map.replace(key, oldValue, newValue);
}
@Override
public V replace(K key, V value) {
checkType(key);
checkType(value);
return copyIfRequired(map.replace(key, value));
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
map.replaceAll((k, v) -> {
checkType(k);
checkType(v);
V output = function.apply(k, v);
checkType(output);
return output;
});
}
@Override
public void close() {
maps.remove(name);
}
@Override
public Set<K> keySet() {
Set<K> keys = new HashSet<>(map.size());
for (K k : map.keySet()) {
keys.add(copyIfRequired(k));
}
return keys;
}
@Override
public Collection<V> values() {
List<V> values = new ArrayList<>(map.size());
for (V v : map.values()) {
values.add(copyIfRequired(v));
}
return values;
}
/**
* Composes the given bi-function ({@code f(a,b)}) with a function checking the type of the output:
* {@code checkType(f(a,b))}. So the output of the given function is checked to verify that it uses a valid type.
*
* @param function the function
* @return the composition
*/
private BiFunction<? super K, ? super V, ? extends V> typeChecked(BiFunction<? super K, ? super V, ? extends V>
function) {
return (k, v) -> {
checkType(k);
V output = function.apply(k, v);
checkType(output);
return output;
};
}
/**
* Composes the given function ({@code f(a)}) with a function checking the type of the output. So the output of the
* given function is checked to verify that is uses a valid type.
*
* @param function the function
* @return the composition
*/
private Function<? super K, ? extends V> typeChecked(Function<? super K, ? extends V>
function) {
return k -> {
checkType(k);
V output = function.apply(k);
checkType(output);
return output;
};
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.compute(key, typeChecked(remappingFunction));
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return map.computeIfAbsent(key, typeChecked(mappingFunction));
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.computeIfPresent(key, typeChecked(remappingFunction));
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> entries = new HashSet<>(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
entries.add(new Map.Entry<K, V>() {
@Override
public K getKey() {
return copyIfRequired(entry.getKey());
}
@Override
public V getValue() {
return copyIfRequired(entry.getValue());
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException();
}
});
}
return entries;
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
// Cannot delegate, it needs to copy the objects to avoid modifications
for (Map.Entry<K, V> entry : entrySet()) {
action.accept(entry.getKey(), entry.getValue());
}
}
@Override
public V getOrDefault(Object key, V defaultValue) {
return copyIfRequired(map.getOrDefault(key, defaultValue));
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
checkType(key);
checkType(value);
return map.merge(key, value, (k, v) -> {
// No need to check the key, already check above.
V output = remappingFunction.apply(k, v);
checkType(output);
return output;
});
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
// Iterate over the set to entry and call `put` on each entry to validate the types
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public String toString() {
return map.toString();
}
}