refactor!: Refactored type hierarchy and methods of StructuredConfigEntry
This commit is contained in:
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `coat-bridge`: Added experimental text mapper based on Tweed Serde.
|
||||
|
||||
### Changed
|
||||
- **Breaking**@`core`: Refactored type hierarchy and methods of `StructuredConfigEntry`.
|
||||
- `weaver-pojo-serde-extension`: Slightly changed the `SerdePojoReaderWriterSpec`
|
||||
to be more closely aligned with Java's identifier rules.
|
||||
- `attributes-extension`: The `AttributesReadWriteFilterExtension` now correctly skips non-matching compound entries
|
||||
|
||||
@@ -6,6 +6,7 @@ plugins {
|
||||
dependencies {
|
||||
implementation(project(":tweed5-core"))
|
||||
compileOnly(project(":tweed5-serde-extension"))
|
||||
testImplementation(project(":tweed5-default-extensions"))
|
||||
testImplementation(project(":tweed5-serde-extension"))
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -16,7 +17,7 @@ public class AttributesFilteredCompoundEntry<T extends @Nullable Object> impleme
|
||||
private final CompoundConfigEntry<T> delegate;
|
||||
|
||||
@Override
|
||||
public <V> void set(T compoundValue, String key, V value) {
|
||||
public void set(T compoundValue, String key, Object value) {
|
||||
if (value == AttributesReadWriteFilterExtensionImpl.NOOP_MARKER) {
|
||||
return;
|
||||
}
|
||||
@@ -24,13 +25,13 @@ public class AttributesFilteredCompoundEntry<T extends @Nullable Object> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(T compoundValue, String key) {
|
||||
public Object get(T compoundValue, String key) {
|
||||
return delegate.get(compoundValue, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T instantiateCompoundValue() {
|
||||
return delegate.instantiateCompoundValue();
|
||||
public @NonNull T instantiateValue() {
|
||||
return delegate.instantiateValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface AddressableStructuredConfigEntry<T> extends StructuredConfigEntry<T> {
|
||||
@Override
|
||||
default AddressableStructuredConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
Object get(T value, String key);
|
||||
}
|
||||
@@ -38,9 +38,9 @@ public interface CollectionConfigEntry<E, T extends Collection<E>> extends Struc
|
||||
int index = 0;
|
||||
for (E item : value) {
|
||||
String indexString = Integer.toString(index);
|
||||
if (visitor.enterStructuredSubEntry("element", indexString)) {
|
||||
if (visitor.enterAddressableStructuredSubEntry("element", indexString, null)) {
|
||||
elementEntry().visitInOrder(visitor, item);
|
||||
visitor.leaveStructuredSubEntry("element", indexString);
|
||||
visitor.leaveAddressableStructuredSubEntry("element", indexString, null);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,13 @@ import de.siphalor.tweed5.core.api.Arity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface CompoundConfigEntry<T> extends StructuredConfigEntry<T> {
|
||||
public interface CompoundConfigEntry<T> extends MutableStructuredConfigEntry<T> {
|
||||
@Override
|
||||
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
MutableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
<V> void set(T compoundValue, String key, V value);
|
||||
<V> V get(T compoundValue, String key);
|
||||
|
||||
T instantiateCompoundValue();
|
||||
|
||||
@Override
|
||||
default void visitInOrder(ConfigEntryVisitor visitor) {
|
||||
if (visitor.enterStructuredEntry(this)) {
|
||||
|
||||
@@ -3,18 +3,25 @@ package de.siphalor.tweed5.core.api.entry;
|
||||
public interface ConfigEntryValueVisitor {
|
||||
<T> void visitEntry(ConfigEntry<T> entry, T value);
|
||||
|
||||
default <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
default <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean enterAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void leaveAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) {
|
||||
}
|
||||
|
||||
default void leaveStructuredSubEntry(String entryKey, String valueKey) {
|
||||
}
|
||||
|
||||
default <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
default <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface MutableStructuredConfigEntry<T> extends AddressableStructuredConfigEntry<T> {
|
||||
@Override
|
||||
default AddressableStructuredConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
AddressableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull T instantiateValue();
|
||||
void set(T value, String key, Object subValue);
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface NullableConfigEntry<T extends @Nullable Object> extends StructuredConfigEntry<T> {
|
||||
public interface NullableConfigEntry<T extends @Nullable Object> extends AddressableStructuredConfigEntry<T> {
|
||||
String NON_NULL_KEY = ":nonNull";
|
||||
|
||||
@Override
|
||||
default NullableConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
StructuredConfigEntry.super.apply(function);
|
||||
AddressableStructuredConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
|
||||
public class CollectionConfigEntryImpl<E, T extends @NonNull Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
|
||||
private final IntFunction<T> collectionConstructor;
|
||||
private final ConfigEntry<E> elementEntry;
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
|
||||
this.nonNullEntry = nonNullEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(T value, String key) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ConfigEntry<?>> subEntries() {
|
||||
return Collections.singletonMap(NON_NULL_KEY, nonNullEntry);
|
||||
@@ -33,9 +38,9 @@ public class NullableConfigEntryImpl<T extends @Nullable Object> extends BaseCon
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (value != null) {
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
if (visitor.enterStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY)) {
|
||||
if (visitor.enterAddressableStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY, NON_NULL_KEY)) {
|
||||
nonNullEntry.visitInOrder(visitor, value);
|
||||
visitor.leaveStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY);
|
||||
visitor.leaveAddressableStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY, NON_NULL_KEY);
|
||||
}
|
||||
visitor.leaveStructuredEntry(this, value);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
|
||||
public class StaticMapCompoundConfigEntryImpl<T extends @NonNull Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
|
||||
private final IntFunction<T> mapConstructor;
|
||||
private final Map<String, ConfigEntry<?>> compoundEntries;
|
||||
|
||||
@@ -28,16 +29,15 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> void set(T compoundValue, String key, V value) {
|
||||
public void set(T compoundValue, String key, Object value) {
|
||||
requireKey(key);
|
||||
compoundValue.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(T compoundValue, String key) {
|
||||
public Object get(T compoundValue, String key) {
|
||||
requireKey(key);
|
||||
//noinspection unchecked
|
||||
return (V) compoundValue.get(key);
|
||||
return compoundValue.get(key);
|
||||
}
|
||||
|
||||
private void requireKey(String key) {
|
||||
@@ -47,29 +47,27 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
||||
}
|
||||
|
||||
@Override
|
||||
public T instantiateCompoundValue() {
|
||||
public T instantiateValue() {
|
||||
return mapConstructor.apply(compoundEntries.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
if (value != null) {
|
||||
compoundEntries.forEach((key, entry) -> {
|
||||
if (visitor.enterStructuredSubEntry(key, key)) {
|
||||
if (visitor.enterAddressableStructuredSubEntry(key, key, key)) {
|
||||
//noinspection unchecked
|
||||
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
||||
visitor.leaveStructuredSubEntry(key, key);
|
||||
visitor.leaveAddressableStructuredSubEntry(key, key, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
visitor.leaveStructuredEntry(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deepCopy(T value) {
|
||||
T copy = instantiateCompoundValue();
|
||||
T copy = instantiateValue();
|
||||
value.forEach((String key, Object element) -> {
|
||||
//noinspection unchecked
|
||||
ConfigEntry<Object> elementEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
||||
|
||||
@@ -62,7 +62,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
||||
if (targetValue != null) {
|
||||
targetCompoundValue = targetValue;
|
||||
} else {
|
||||
targetCompoundValue = compoundEntry.instantiateCompoundValue();
|
||||
targetCompoundValue = compoundEntry.instantiateValue();
|
||||
}
|
||||
compoundEntry.subEntries().forEach((key, subEntry) -> {
|
||||
if (!patchInfo.containsEntry(subEntry)) {
|
||||
@@ -70,7 +70,7 @@ public class PatchExtensionImpl implements PatchExtension, ReadWriteRelatedExten
|
||||
}
|
||||
compoundEntry.set(
|
||||
targetCompoundValue, key, patch(
|
||||
subEntry,
|
||||
(ConfigEntry<Object>) subEntry,
|
||||
compoundEntry.get(targetCompoundValue, key),
|
||||
compoundEntry.get(patchValue, key),
|
||||
patchInfo
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.siphalor.tweed5.defaultextensions.pather.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -16,10 +17,19 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
return delegate.enterStructuredEntry(entry, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterAddressableStructuredSubEntry(String entryKey, String valueKey, String dataKey) {
|
||||
boolean enter = delegate.enterAddressableStructuredSubEntry(entryKey, valueKey, dataKey);
|
||||
if (enter) {
|
||||
pathTracking.pushPathPart(entryKey, valueKey);
|
||||
}
|
||||
return enter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterStructuredSubEntry(String entryKey, String valueKey) {
|
||||
boolean enter = delegate.enterStructuredSubEntry(entryKey, valueKey);
|
||||
@@ -29,6 +39,12 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
|
||||
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);
|
||||
@@ -36,7 +52,7 @@ public class PathTrackingConfigEntryValueVisitor implements ConfigEntryValueVisi
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
delegate.leaveStructuredEntry(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.siphalor.tweed5.defaultextensions.validation.impl;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.StructuredConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
@@ -290,12 +291,12 @@ public class ValidationExtensionImpl implements ReadWriteRelatedExtension, Valid
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean enterStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> boolean enterStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void leaveStructuredEntry(ConfigEntry<T> entry, T value) {
|
||||
public <T> void leaveStructuredEntry(StructuredConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ public class TweedEntryReaderWriterImpls {
|
||||
}
|
||||
|
||||
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
||||
T compoundValue = entry.instantiateCompoundValue();
|
||||
T compoundValue = entry.instantiateValue();
|
||||
while (true) {
|
||||
try {
|
||||
TweedDataToken token = reader.readToken();
|
||||
|
||||
@@ -37,7 +37,7 @@ public class DefaultPresetWeavingProcessor<T> implements TweedPojoWeavingExtensi
|
||||
|
||||
private T instantiateEntry(ConfigEntry<T> entry) {
|
||||
if (entry instanceof CompoundConfigEntry) {
|
||||
return ((CompoundConfigEntry<T>) entry).instantiateCompoundValue();
|
||||
return ((CompoundConfigEntry<T>) entry).instantiateValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Can only determine default preset from instantiation for POJOs. "
|
||||
|
||||
@@ -6,7 +6,6 @@ import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -41,7 +40,7 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> void set(T compoundValue, String key, V value) {
|
||||
public void set(T compoundValue, String key, Object value) {
|
||||
SubEntry subEntry = subEntries.get(key);
|
||||
if (subEntry == null) {
|
||||
throw new IllegalArgumentException("Unknown config entry: " + key);
|
||||
@@ -55,22 +54,21 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(T compoundValue, String key) {
|
||||
public Object get(T compoundValue, String key) {
|
||||
SubEntry subEntry = subEntries.get(key);
|
||||
if (subEntry == null) {
|
||||
throw new IllegalArgumentException("Unknown config entry: " + key);
|
||||
}
|
||||
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (V) subEntry.getter().invoke(compoundValue);
|
||||
return subEntry.getter().invoke(compoundValue);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("Failed to get value for config entry \"" + key + "\"", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T instantiateCompoundValue() {
|
||||
public T instantiateValue() {
|
||||
try {
|
||||
return noArgsConstructor.get();
|
||||
} catch (Throwable e) {
|
||||
@@ -82,22 +80,24 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterStructuredEntry(this, value)) {
|
||||
subEntries.forEach((key, entry) -> {
|
||||
if (visitor.enterStructuredSubEntry(key, key)) {
|
||||
if (visitor.enterAddressableStructuredSubEntry(key, key, key)) {
|
||||
try {
|
||||
Object subValue = entry.getter().invoke(value);
|
||||
//noinspection unchecked
|
||||
((ConfigEntry<Object>) entry.configEntry()).visitInOrder(visitor, subValue);
|
||||
visitor.leaveAddressableStructuredSubEntry(key, key, key);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to get compound sub entry value \"" + key + "\"");
|
||||
}
|
||||
}
|
||||
});
|
||||
visitor.leaveStructuredEntry(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deepCopy(T value) {
|
||||
T copy = instantiateCompoundValue();
|
||||
T copy = instantiateValue();
|
||||
for (SubEntry subEntry : subEntries.values()) {
|
||||
try {
|
||||
Object subValue = subEntry.getter().invoke(value);
|
||||
|
||||
Reference in New Issue
Block a user