diff --git a/run/resourcepacks/testpack/assets/minecraft/langer/en_us.json.rpo b/run/resourcepacks/testpack/assets/minecraft/langer/en_us.json.rpo index ebb456f..72d43b0 100644 --- a/run/resourcepacks/testpack/assets/minecraft/langer/en_us.json.rpo +++ b/run/resourcepacks/testpack/assets/minecraft/langer/en_us.json.rpo @@ -2,14 +2,10 @@ "conditions": [ "lumi:subcategoryTest.enableLang", { - "not": [ - "subcategoryTest.enableLangForceDisable" - ] + "not": "subcategoryTest.enableLangForceDisable" } ], - "fallbacks": [ - "assets/minecraft/lang/en_us_joke.json" - ], + "fallback": "assets/minecraft/lang/en_us_joke.json", "expansions": { "Lights": "{lumi.subcategoryTest.enableLang}", "Mode": "{lumi.debugMode}", diff --git a/src/main/java/io/gitlab/jfronny/respackopts/RpoModInfo.java b/src/main/java/io/gitlab/jfronny/respackopts/RpoModInfo.java index 4ce0a54..447d884 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/RpoModInfo.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/RpoModInfo.java @@ -34,9 +34,10 @@ public class RpoModInfo { .registerTypeAdapter(ConfigBooleanEntry.class, new BooleanEntrySerializer()) .registerTypeAdapter(ConfigBranch.class, new ConfigBranchSerializer()) .registerTypeAdapter(Script.class, new ScriptDeserializer()) - .registerTypeAdapter(FileRpo.class, new RpoDeserializer()) + .registerTypeAdapter(FileRpo.class, new FileRpoDeserializer()) .registerTypeAdapter(DirRpo.class, new DirRpoDeserializer()) .registerTypeAdapter(Condition.class, new ConditionDeserializer()) + .registerTypeAdapterFactory(new SingleElementSetTypeAdapterFactory()) .setPrettyPrinting() .create(); try { diff --git a/src/main/java/io/gitlab/jfronny/respackopts/data/FileRpo.java b/src/main/java/io/gitlab/jfronny/respackopts/data/FileRpo.java index b485049..d06ed18 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/data/FileRpo.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/data/FileRpo.java @@ -3,11 +3,11 @@ package io.gitlab.jfronny.respackopts.data; import io.gitlab.jfronny.respackopts.data.condition.Condition; import meteordevelopment.starscript.Script; -import java.util.List; import java.util.Map; +import java.util.Set; public class FileRpo { public Condition conditions; - public List fallbacks; + public Set fallbacks; public Map expansions; } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java index 2d9f7b2..7061a0a 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/filters/util/FileFallbackProvider.java @@ -8,14 +8,12 @@ import net.minecraft.util.Identifier; import java.io.InputStream; import java.util.Collection; -import java.util.List; public class FileFallbackProvider { public static boolean fileVisible(WrappedPack pack, String name) { return FileRpoSearchProvider.modifyWithRpo(name, pack, rpo -> { if (rpo.fallbacks != null) { - List arr = rpo.fallbacks; - for (String s : arr) { + for (String s : rpo.fallbacks) { ResourcePath tmp = new ResourcePath(s); if (pack.contains(tmp.getType(), tmp.getId())) return true; @@ -29,8 +27,7 @@ public class FileFallbackProvider { return FileRpoSearchProvider.modifyWithRpo(name, pack, rpo -> { try { if (rpo.fallbacks != null) { - List arr = rpo.fallbacks; - for (String s : arr) { + for (String s : rpo.fallbacks) { ResourcePath tmp = new ResourcePath(s); if (pack.contains(tmp.getType(), tmp.getId())) return pack.open(tmp.getType(), tmp.getId()); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/gson/DirRpoDeserializer.java b/src/main/java/io/gitlab/jfronny/respackopts/gson/DirRpoDeserializer.java index e628c43..b5836d1 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/gson/DirRpoDeserializer.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/gson/DirRpoDeserializer.java @@ -15,19 +15,15 @@ public class DirRpoDeserializer implements JsonDeserializer { DirRpo rpo = new DirRpo(); for (Map.Entry entry : json.getAsJsonObject().entrySet()) { switch (entry.getKey()) { - case "conditions": - case "condition": - rpo.conditions = context.deserialize(entry.getValue(), Condition.class); - break; - case "fallbacks": - case "fallback": + case "conditions", "condition" -> rpo.conditions = context.deserialize(entry.getValue(), Condition.class); + case "fallbacks", "fallback" -> { if (entry.getValue().isJsonPrimitive() && entry.getValue().getAsJsonPrimitive().isString()) { rpo.fallback = entry.getValue().getAsString(); } else { throw new JsonParseException("Directory .rpos only support a single fallback"); } - break; + } } } return rpo; diff --git a/src/main/java/io/gitlab/jfronny/respackopts/gson/FileRpoDeserializer.java b/src/main/java/io/gitlab/jfronny/respackopts/gson/FileRpoDeserializer.java new file mode 100644 index 0000000..430245e --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/gson/FileRpoDeserializer.java @@ -0,0 +1,30 @@ +package io.gitlab.jfronny.respackopts.gson; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import io.gitlab.jfronny.respackopts.data.FileRpo; +import io.gitlab.jfronny.respackopts.data.condition.Condition; +import meteordevelopment.starscript.Script; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; + +public class FileRpoDeserializer implements JsonDeserializer { + private static final Type stringScriptMapType = new TypeToken>(){}.getType(); + private static final Type stringSetType = new TypeToken>(){}.getType(); + @Override + public FileRpo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (!json.isJsonObject()) + throw new JsonParseException("Rpo must be a json object"); + FileRpo rpo = new FileRpo(); + for (Map.Entry entry : json.getAsJsonObject().entrySet()) { + switch (entry.getKey()) { + case "conditions", "condition" -> rpo.conditions = context.deserialize(entry.getValue(), Condition.class); + case "fallbacks", "fallback" -> rpo.fallbacks = context.deserialize(entry.getValue(), stringSetType); + case "expansions", "expansion" -> rpo.expansions = context.deserialize(entry.getValue(), stringScriptMapType); + } + } + return rpo; + } +} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/gson/RpoDeserializer.java b/src/main/java/io/gitlab/jfronny/respackopts/gson/RpoDeserializer.java deleted file mode 100644 index ea0aa20..0000000 --- a/src/main/java/io/gitlab/jfronny/respackopts/gson/RpoDeserializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.gitlab.jfronny.respackopts.gson; - -import com.google.gson.*; -import com.google.gson.reflect.TypeToken; -import io.gitlab.jfronny.respackopts.data.FileRpo; -import io.gitlab.jfronny.respackopts.data.condition.Condition; -import meteordevelopment.starscript.Script; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class RpoDeserializer implements JsonDeserializer { - @Override - public FileRpo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - if (!json.isJsonObject()) - throw new JsonParseException("Rpo must be a json object"); - FileRpo rpo = new FileRpo(); - for (Map.Entry entry : json.getAsJsonObject().entrySet()) { - switch (entry.getKey()) { - case "conditions": - case "condition": - rpo.conditions = context.deserialize(entry.getValue(), Condition.class); - break; - case "fallbacks": - case "fallback": - if (entry.getValue().isJsonPrimitive() && entry.getValue().getAsJsonPrimitive().isString()) { - rpo.fallbacks = new ArrayList<>(); - rpo.fallbacks.add(entry.getValue().getAsString()); - } - else { - Type listType = new TypeToken>(){}.getType(); - rpo.fallbacks = context.deserialize(entry.getValue(), listType); - } - break; - case "expansions": - case "expansion": - Type listType = new TypeToken>(){}.getType(); - rpo.expansions = context.deserialize(entry.getValue(), listType); - break; - } - } - return rpo; - } -} diff --git a/src/main/java/io/gitlab/jfronny/respackopts/gson/SingleElementSetTypeAdapterFactory.java b/src/main/java/io/gitlab/jfronny/respackopts/gson/SingleElementSetTypeAdapterFactory.java new file mode 100644 index 0000000..bdad73e --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/respackopts/gson/SingleElementSetTypeAdapterFactory.java @@ -0,0 +1,66 @@ +package io.gitlab.jfronny.respackopts.gson; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import io.gitlab.jfronny.respackopts.RpoModInfo; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.LinkedHashSet; +import java.util.Set; + +public class SingleElementSetTypeAdapterFactory implements TypeAdapterFactory { + @Override + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Type type = typeToken.getType(); + if (typeToken.getRawType() != Set.class + || !(type instanceof ParameterizedType pt)) + return null; + if (RpoModInfo.CONFIG.debugLogs) + RpoModInfo.LOGGER.info("Using SingleElementSetTypeAdapter for " + typeToken.toString()); + Type elementType = pt.getActualTypeArguments()[0]; + TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType)); + return (TypeAdapter) createAdapter(elementAdapter); + } + + private TypeAdapter> createAdapter(final TypeAdapter elementAdapter) { + return new TypeAdapter<>() { + @Override + public void write(JsonWriter out, Set value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + + out.beginArray(); + for (T entry : value) { + elementAdapter.write(out, entry); + } + out.endArray(); + } + + @Override + public Set read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + Set list = new LinkedHashSet<>(); + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + while (in.hasNext()) { + list.add(elementAdapter.read(in)); + } + in.endArray(); + } + else list.add(elementAdapter.read(in)); + return list; + } + }; + } +} diff --git a/src/test/java/io/gitlab/jfronny/respackopts/RespackoptsTest.java b/src/test/java/io/gitlab/jfronny/respackopts/RespackoptsTest.java index 89b1810..7bb370f 100644 --- a/src/test/java/io/gitlab/jfronny/respackopts/RespackoptsTest.java +++ b/src/test/java/io/gitlab/jfronny/respackopts/RespackoptsTest.java @@ -1,5 +1,7 @@ package io.gitlab.jfronny.respackopts; +import com.google.gson.reflect.TypeToken; +import io.gitlab.jfronny.respackopts.data.ConfigFile; import io.gitlab.jfronny.respackopts.data.entry.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -8,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Set; import static io.gitlab.jfronny.respackopts.Respackopts.*; import static io.gitlab.jfronny.respackopts.RpoModInfo.*; @@ -22,6 +25,7 @@ class RespackoptsTest { static void initialize() { LOGGER.info("Expected error end"); CONF_DIR = Paths.get("./test"); + CONFIG = new ConfigFile(); assertDoesNotThrow(() -> Files.deleteIfExists(CONF_DIR.resolve(testEntry1Name + ".json"))); assertDoesNotThrow(() -> Files.createDirectories(CONF_DIR)); SAVE_ACTIONS.add(() -> LOGGER.info("Save")); @@ -94,4 +98,25 @@ class RespackoptsTest { cbNew.add(testEntryName, new ConfigBooleanEntry(true)); cbNew.sync(cb, SyncMode.RESPACK_LOAD); } + + @Test + void gsonSetOrder() { + String[] expected = new String[] {"one", "two", "three", "zero", "76"}; + String gson = "[\"one\", \"two\", \"three\", \"zero\", \"76\"]"; + Set parsed = GSON.fromJson(gson, new TypeToken>(){}.getType()); + int i = 0; + for (String s : parsed) { + assertEquals(s, expected[i++]); + } + } + + @Test + void gsonSingleEntrySet() { + String gson = "\"someText\""; + Set parsed = GSON.fromJson(gson, new TypeToken>(){}.getType()); + assertEquals(parsed.size(), 1); + for (String s : parsed) { + assertEquals(s, "someText"); + } + } }