package org.embeddedt.modernfix.dynresources; import com.google.common.collect.Collections2; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; /** * A map that behaves like Guava's Maps.asMap but allows for additional entries to be written that override the backing * map's entries. */ public final class DynamicRegistryMap implements Map { private static final Object NULL_OVERRIDE = new Object(); private final Set originalKeys; private final Function fallbackGetter; private final ConcurrentHashMap overrides; private final EntrySet entrySet; public DynamicRegistryMap(Set originalKeys, Function fallbackGetter) { this.originalKeys = originalKeys; this.fallbackGetter = fallbackGetter; this.overrides = new ConcurrentHashMap<>(); this.entrySet = new EntrySet(); } @Override public int size() { return originalKeys.size(); } @Override public boolean isEmpty() { return originalKeys.isEmpty(); } @Override public boolean containsKey(Object o) { if (o == null) { return false; } var override = overrides.get(o); if (override == NULL_OVERRIDE) { return false; } return override != null || originalKeys.contains(o); } @Override public boolean containsValue(Object o) { if (o == null || o == NULL_OVERRIDE) { return false; } return overrides.containsValue(o); } @Override public V get(Object o) { Object value = overrides.get(o); if (value == NULL_OVERRIDE) { return null; } else if (value != null) { return (V) value; } else { return fallbackGetter.apply((K)o); } } @Override public @Nullable V put(K k, V v) { if (v == null) { return remove(k); } overrides.put(k, v); return null; } @Override public V remove(Object o) { overrides.put((K)o, NULL_OVERRIDE); return null; } @Override public void putAll(@NotNull Map map) { map.forEach(this::put); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public @NotNull Set keySet() { return Collections.unmodifiableSet(originalKeys); } @Override public @NotNull Collection values() { return Collections2.transform(originalKeys, this::get); } @Override public @NotNull Set> entrySet() { return this.entrySet; } private class ModelEntry implements Map.Entry { private final K key; private ModelEntry(K key) { this.key = key; } @Override public K getKey() { return key; } @Override public V getValue() { return get(key); } @Override public V setValue(V value) { return put(key, value); } } private class EntrySet extends AbstractSet> { @Override public Iterator> iterator() { var iterator = originalKeys.iterator(); return new Iterator<>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public Entry next() { return new ModelEntry(iterator.next()); } }; } @Override public int size() { return DynamicRegistryMap.this.size(); } } }