170 lines
4.8 KiB
Java
170 lines
4.8 KiB
Java
package io.gitlab.jfronny.respackopts.util;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.util.*;
|
|
import java.util.stream.Stream;
|
|
|
|
public final class OverlayMap<K, V> extends AbstractMap<K, V> {
|
|
private final Map<K, V> overlay;
|
|
private final Map<K, V> lower;
|
|
private final Set<K> masked = new HashSet<>();
|
|
|
|
public OverlayMap(Map<K, V> overlay, Map<K, V> lower) {
|
|
this.overlay = overlay;
|
|
this.lower = lower;
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
return overlay.size() + (int) lower.keySet().stream()
|
|
.filter(s -> !overlay.containsKey(s) && !masked.contains(s))
|
|
.count();
|
|
}
|
|
|
|
private Stream<K> streamKeys() {
|
|
return Stream.concat(
|
|
overlay.keySet().stream(),
|
|
lower.keySet().stream()
|
|
.filter(s -> !overlay.containsKey(s) && !masked.contains(s))
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty() {
|
|
return overlay.isEmpty() && (masked.size() >= lower.size());
|
|
}
|
|
|
|
@Override
|
|
public boolean containsKey(Object key) {
|
|
return overlay.containsKey(key) || (lower.containsKey(key) && !masked.contains(key));
|
|
}
|
|
|
|
@Override
|
|
public boolean containsValue(Object value) {
|
|
return overlay.containsValue(value) || lower.entrySet()
|
|
.stream()
|
|
.filter(s -> Objects.equals(s.getValue(), value))
|
|
.anyMatch(s -> !masked.contains(s.getKey()));
|
|
}
|
|
|
|
@Override
|
|
public V get(Object key) {
|
|
V result = overlay.get(key);
|
|
if (result == null && !masked.contains(key)) result = lower.get(key);
|
|
return result;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public V put(K key, V value) {
|
|
V result = overlay.put(key, value);
|
|
V low = lower.get(key);
|
|
if (low != null && masked.add(key) && result == null) {
|
|
result = low;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public V remove(Object key) {
|
|
V result = overlay.remove(key);
|
|
V low = lower.get(key);
|
|
if (low != null && masked.add((K) key) && result == null) {
|
|
result = low;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void clear() {
|
|
overlay.clear();
|
|
masked.addAll(lower.keySet());
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<Entry<K, V>> entrySet() {
|
|
return entrySet;
|
|
}
|
|
|
|
private final Set<Entry<K, V>> entrySet = new AbstractSet<>() {
|
|
@Override
|
|
public int size() {
|
|
return OverlayMap.this.size();
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty() {
|
|
return OverlayMap.this.isEmpty();
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(Object o) {
|
|
return o instanceof Map.Entry<?,?> e && Objects.equals(OverlayMap.this.get(e.getKey()), e.getValue());
|
|
}
|
|
|
|
@Override
|
|
public boolean add(Entry<K, V> kvEntry) {
|
|
return OverlayMap.this.put(kvEntry.getKey(), kvEntry.getValue()) == null;
|
|
}
|
|
|
|
@Override
|
|
public boolean remove(Object o) {
|
|
return o instanceof Map.Entry<?,?> e && OverlayMap.this.remove(e.getKey(), e.getValue());
|
|
}
|
|
|
|
@Override
|
|
public void clear() {
|
|
OverlayMap.this.clear();
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Iterator<Entry<K, V>> iterator() {
|
|
return new Iterator<>() {
|
|
private final Iterator<K> keys = OverlayMap.this.streamKeys().iterator();
|
|
private K previous = null;
|
|
@Override
|
|
public boolean hasNext() {
|
|
return keys.hasNext();
|
|
}
|
|
|
|
@Override
|
|
public Entry<K, V> next() {
|
|
previous = keys.next();
|
|
return new Entry<>() {
|
|
private final K currentK = previous;
|
|
private V currentV = OverlayMap.this.get(currentK);
|
|
|
|
@Override
|
|
public K getKey() {
|
|
return currentK;
|
|
}
|
|
|
|
@Override
|
|
public V getValue() {
|
|
return currentV;
|
|
}
|
|
|
|
@Override
|
|
public V setValue(V value) {
|
|
V result = OverlayMap.this.put(currentK, value);
|
|
currentV = value;
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
if (previous == null) throw new IllegalStateException();
|
|
OverlayMap.this.remove(previous);
|
|
previous = null;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|