fix: implement OverlayMap to store FallbackI18n without evaluating lower map early
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/jfmod Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-11-19 15:06:02 +01:00
parent 4943127ecc
commit 2c66bc4146
Signed by: Johannes
GPG Key ID: E76429612C2929F4
2 changed files with 172 additions and 3 deletions

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.respackopts.mixin;
import com.google.common.collect.ImmutableMap;
import io.gitlab.jfronny.respackopts.util.FallbackI18n;
import io.gitlab.jfronny.respackopts.util.OverlayMap;
import net.minecraft.client.resource.language.LanguageManager;
import net.minecraft.client.resource.language.TranslationStorage;
import net.minecraft.resource.ResourceManager;
@ -18,8 +18,8 @@ public class LanguageManagerMixin {
@Inject(method = "reload(Lnet/minecraft/resource/ResourceManager;)V", at = @At("TAIL"), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void rpo$appendTranslations(ResourceManager manager, CallbackInfo ci, List<String> list, boolean bl, TranslationStorage translationStorage) {
TranslationStorageAccessor storage = (TranslationStorageAccessor) translationStorage;
Map<String, String> map = new HashMap<>(storage.getTranslations());
Map<String, String> map = new HashMap<>();
FallbackI18n.insertInto(map);
storage.setTranslations(ImmutableMap.copyOf(map));
storage.setTranslations(new OverlayMap<>(storage.getTranslations(), map));
}
}

View File

@ -0,0 +1,169 @@
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;
}
};
}
};
}