refactor!(core): Simplify ConfigEntryValueVisitor by introducing SubEntryKey instead of passing values separately

This commit is contained in:
2026-04-13 23:26:40 +02:00
parent 2f4011c144
commit 52e2a52468
12 changed files with 135 additions and 57 deletions

View File

@@ -9,5 +9,5 @@ public interface AddressableStructuredConfigEntry<T> extends StructuredConfigEnt
return this; return this;
} }
Object get(T value, String key); Object get(T value, String dataKey);
} }

View File

@@ -37,10 +37,10 @@ public interface CollectionConfigEntry<E, T extends Collection<E>> extends Struc
if (visitor.enterStructuredEntry(this, value)) { if (visitor.enterStructuredEntry(this, value)) {
int index = 0; int index = 0;
for (E item : value) { for (E item : value) {
String indexString = Integer.toString(index); SubEntryKey subEntryKey = SubEntryKey.structured("element", Integer.toString(index));
if (visitor.enterAddressableStructuredSubEntry("element", indexString, null)) { if (visitor.enterSubEntry(subEntryKey)) {
elementEntry().visitInOrder(visitor, item); elementEntry().visitInOrder(visitor, item);
visitor.leaveAddressableStructuredSubEntry("element", indexString, null); visitor.leaveSubEntry(subEntryKey);
} }
index++; index++;
} }

View File

@@ -8,18 +8,11 @@ public interface ConfigEntryValueVisitor {
return true; return true;
} }
default boolean enterAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) { default boolean enterSubEntry(SubEntryKey subEntryKey) {
return true; return true;
} }
default boolean enterStructuredSubEntry(String entryKey, String valueKey) { default void leaveSubEntry(SubEntryKey subEntryKey) {
return true;
}
default void leaveAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) {
}
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
} }
default <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) { default <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {

View File

@@ -12,5 +12,5 @@ public interface MutableStructuredConfigEntry<T> extends AddressableStructuredCo
} }
@NonNull T instantiateValue(); @NonNull T instantiateValue();
void set(T value, String key, Object subValue); void set(T value, String dataKey, Object subValue);
} }

View File

@@ -0,0 +1,53 @@
package de.siphalor.tweed5.core.api.entry;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Value;
import org.jspecify.annotations.Nullable;
@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class SubEntryKey {
String entry;
@Nullable String value;
@Nullable String data;
/**
* Models the key of a transparent entry. Transparent entries are sub entries that only exist in the entry tree
* but are not actually present in the data.
* @param entryKey the key of the entry in the entry tree
* @apiNote The resulting key is not addressable.
*/
public static SubEntryKey transparent(String entryKey) {
return new SubEntryKey(entryKey, null, null);
}
/**
* Models the key of a transparent entry with a data key for use with {@link AddressableStructuredConfigEntry}s.
* @param entryKey the key of the entry in the entry tree
* @param dataKey the "address" of the data in the {@link AddressableStructuredConfigEntry}
*/
public static SubEntryKey transparentAddressable(String entryKey, String dataKey) {
return new SubEntryKey(entryKey, null, dataKey);
}
/**
* A "normal" sub entry key.
* @param entryKey the key of the entry in the entry tree
* @param valueKey a potentially differing key for the user-facing data tree
*/
public static SubEntryKey structured(String entryKey, String valueKey) {
return new SubEntryKey(entryKey, valueKey, null);
}
/**
* A sub entry key for {@link AddressableStructuredConfigEntry}s.
* @param entryKey the key of the entry in the entry tree
* @param valueKey a potentially differing key for the user-facing data tree
* @param dataKey the "address" of the data in the {@link AddressableStructuredConfigEntry}
* @apiNote {@code valueKey} and {@code dataKey} are usually a 1:1 mapping.
*/
public static SubEntryKey addressable(String entryKey, String valueKey, String dataKey) {
return new SubEntryKey(entryKey, valueKey, dataKey);
}
}

View File

@@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor; import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; import de.siphalor.tweed5.core.api.entry.NullableConfigEntry;
import de.siphalor.tweed5.core.api.entry.SubEntryKey;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -25,7 +26,7 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
} }
@Override @Override
public T get(T value, String key) { public T get(T value, String dataKey) {
return value; return value;
} }
@@ -38,9 +39,10 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (value != null) { if (value != null) {
if (visitor.enterStructuredEntry(this, value)) { if (visitor.enterStructuredEntry(this, value)) {
if (visitor.enterAddressableStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY, NON_NULL_KEY)) { SubEntryKey subEntryKey = SubEntryKey.transparentAddressable(NON_NULL_KEY, NON_NULL_KEY);
if (visitor.enterSubEntry(subEntryKey)) {
nonNullEntry.visitInOrder(visitor, value); nonNullEntry.visitInOrder(visitor, value);
visitor.leaveAddressableStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY, NON_NULL_KEY); visitor.leaveSubEntry(subEntryKey);
} }
visitor.leaveStructuredEntry(this, value); visitor.leaveStructuredEntry(this, value);
} }

View File

@@ -29,15 +29,15 @@ public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Obj
} }
@Override @Override
public void set(T compoundValue, String key, Object value) { public void set(T compoundValue, String dataKey, Object value) {
requireKey(key); requireKey(dataKey);
compoundValue.put(key, value); compoundValue.put(dataKey, value);
} }
@Override @Override
public Object get(T compoundValue, String key) { public Object get(T compoundValue, String dataKey) {
requireKey(key); requireKey(dataKey);
return compoundValue.get(key); return compoundValue.get(dataKey);
} }
private void requireKey(String key) { private void requireKey(String key) {
@@ -55,10 +55,11 @@ public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Obj
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterStructuredEntry(this, value)) { if (visitor.enterStructuredEntry(this, value)) {
compoundEntries.forEach((key, entry) -> { compoundEntries.forEach((key, entry) -> {
if (visitor.enterAddressableStructuredSubEntry(key, key, key)) { SubEntryKey subEntryKey = SubEntryKey.addressable(key, key, key);
if (visitor.enterSubEntry(subEntryKey)) {
//noinspection unchecked //noinspection unchecked
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key)); ((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
visitor.leaveAddressableStructuredSubEntry(key, key, key); visitor.leaveSubEntry(subEntryKey);
} }
}); });
visitor.leaveStructuredEntry(this, value); visitor.leaveStructuredEntry(this, value);

View File

@@ -9,7 +9,11 @@ public interface PathTracking {
void pushPathPart(String pathPart); void pushPathPart(String pathPart);
void pushEmptyPathPart();
void popPathPart(); void popPathPart();
String currentPath(); String currentPath();
String[] currentPathParts();
} }

View File

@@ -3,6 +3,7 @@ package de.siphalor.tweed5.defaultextensions.pather.api;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor; import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry; import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry;
import de.siphalor.tweed5.core.api.entry.SubEntryKey;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
@@ -22,32 +23,21 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
} }
@Override @Override
public boolean enterAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) { public boolean enterSubEntry(SubEntryKey subEntryKey) {
boolean enter = delegate.enterAddressableStructuredSubEntry(entryKey, valueKey, dataKey); boolean enter = delegate.enterSubEntry(subEntryKey);
if (enter) { if (enter) {
pathTracking.pushPathPart(entryKey, valueKey); if (subEntryKey.value() != null) {
pathTracking.pushPathPart(subEntryKey.entry(), subEntryKey.value());
} else {
pathTracking.pushEmptyValuePathPart(subEntryKey.entry());
}
} }
return enter; return enter;
} }
@Override @Override
public boolean enterStructuredSubEntry(String entryKey, String valueKey) { public void leaveSubEntry(SubEntryKey subEntryKey) {
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey); delegate.leaveSubEntry(subEntryKey);
if (enter) {
pathTracking.pushPathPart(entryKey, valueKey);
}
return enter;
}
@Override
public void leaveAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) {
delegate.leaveAddressableStructuredSubEntry(entryKey, valueKey, dataKey);
pathTracking.popPathPart();
}
@Override
public void leaveStructuredSubEntry(String entryKey, String valueKey) {
delegate.leaveStructuredSubEntry(entryKey, valueKey);
pathTracking.popPathPart(); pathTracking.popPathPart();
} }

View File

@@ -17,6 +17,17 @@ public final class ValuePathTracking implements PathTracking {
valuePathTracking.pushPathPart(valuePathPart); valuePathTracking.pushPathPart(valuePathPart);
} }
@Override
public void pushEmptyPathPart() {
entryPathTracking.pushEmptyPathPart();
valuePathTracking.pushEmptyPathPart();
}
public void pushEmptyValuePathPart(String entryPathPart) {
entryPathTracking.pushPathPart(entryPathPart);
valuePathTracking.pushEmptyPathPart();
}
@Override @Override
public void popPathPart() { public void popPathPart() {
valuePathTracking.popPathPart(); valuePathTracking.popPathPart();
@@ -31,4 +42,13 @@ public final class ValuePathTracking implements PathTracking {
public String currentEntryPath() { public String currentEntryPath() {
return entryPathTracking.currentPath(); return entryPathTracking.currentPath();
} }
@Override
public String[] currentPathParts() {
return valuePathTracking.currentPathParts();
}
public String[] currentEntryPathParts() {
return entryPathTracking.currentPathParts();
}
} }

View File

@@ -1,13 +1,14 @@
package de.siphalor.tweed5.defaultextensions.pather.impl; package de.siphalor.tweed5.defaultextensions.pather.impl;
import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking; import de.siphalor.tweed5.defaultextensions.pather.api.PathTracking;
import org.jspecify.annotations.Nullable;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
public class PathTrackingImpl implements PathTracking { public class PathTrackingImpl implements PathTracking {
private final StringBuilder pathBuilder = new StringBuilder(256); private final StringBuilder pathBuilder = new StringBuilder(256);
private final Deque<String> pathParts = new ArrayDeque<>(50); private final Deque<@Nullable String> pathParts = new ArrayDeque<>(50);
@Override @Override
public void pushPathPart(String entryPathPart) { public void pushPathPart(String entryPathPart) {
@@ -15,16 +16,28 @@ public class PathTrackingImpl implements PathTracking {
pathBuilder.append(".").append(entryPathPart); pathBuilder.append(".").append(entryPathPart);
} }
@Override
public void pushEmptyPathPart() {
pathParts.push(null);
}
@Override @Override
public void popPathPart() { public void popPathPart() {
if (!pathParts.isEmpty()) { if (!pathParts.isEmpty()) {
String poppedPart = pathParts.pop(); String poppedPart = pathParts.pop();
if (poppedPart != null) {
pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1); pathBuilder.setLength(pathBuilder.length() - poppedPart.length() - 1);
} }
} }
}
@Override @Override
public String currentPath() { public String currentPath() {
return pathBuilder.toString(); return pathBuilder.toString();
} }
@Override
public String[] currentPathParts() {
return pathParts.toArray(new String[0]);
}
} }

View File

@@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer;
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry; import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor; import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
import de.siphalor.tweed5.core.api.entry.SubEntryKey;
import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry; import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry;
import java.util.Collections; import java.util.Collections;
@@ -40,30 +41,30 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
} }
@Override @Override
public void set(T compoundValue, String key, Object value) { public void set(T compoundValue, String dataKey, Object value) {
SubEntry subEntry = subEntries.get(key); SubEntry subEntry = subEntries.get(dataKey);
if (subEntry == null) { if (subEntry == null) {
throw new IllegalArgumentException("Unknown config entry: " + key); throw new IllegalArgumentException("Unknown config entry: " + dataKey);
} }
try { try {
subEntry.setter().invoke(compoundValue, value); subEntry.setter().invoke(compoundValue, value);
} catch (Throwable e) { } catch (Throwable e) {
throw new IllegalStateException("Failed to set value for config entry \"" + key + "\"", e); throw new IllegalStateException("Failed to set value for config entry \"" + dataKey + "\"", e);
} }
} }
@Override @Override
public Object get(T compoundValue, String key) { public Object get(T compoundValue, String dataKey) {
SubEntry subEntry = subEntries.get(key); SubEntry subEntry = subEntries.get(dataKey);
if (subEntry == null) { if (subEntry == null) {
throw new IllegalArgumentException("Unknown config entry: " + key); throw new IllegalArgumentException("Unknown config entry: " + dataKey);
} }
try { try {
return subEntry.getter().invoke(compoundValue); return subEntry.getter().invoke(compoundValue);
} catch (Throwable e) { } catch (Throwable e) {
throw new IllegalStateException("Failed to get value for config entry \"" + key + "\"", e); throw new IllegalStateException("Failed to get value for config entry \"" + dataKey + "\"", e);
} }
} }
@@ -80,12 +81,13 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterStructuredEntry(this, value)) { if (visitor.enterStructuredEntry(this, value)) {
subEntries.forEach((key, entry) -> { subEntries.forEach((key, entry) -> {
if (visitor.enterAddressableStructuredSubEntry(key, key, key)) { SubEntryKey subEntryKey = SubEntryKey.addressable(key, key, key);
if (visitor.enterSubEntry(subEntryKey)) {
try { try {
Object subValue = entry.getter().invoke(value); Object subValue = entry.getter().invoke(value);
//noinspection unchecked //noinspection unchecked
((ConfigEntry<Object>) entry.configEntry()).visitInOrder(visitor, subValue); ((ConfigEntry<Object>) entry.configEntry()).visitInOrder(visitor, subValue);
visitor.leaveAddressableStructuredSubEntry(key, key, key); visitor.leaveSubEntry(subEntryKey);
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException("Failed to get compound sub entry value \"" + key + "\""); throw new RuntimeException("Failed to get compound sub entry value \"" + key + "\"");
} }