From 9b8eeb45335ccc8fcae497c8577f00f4b40ec04c Mon Sep 17 00:00:00 2001 From: GiantLuigi4 <49770992+GiantLuigi4@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:33:03 -0400 Subject: [PATCH 1/4] Toml --- .../java/turniplabs/halplibe/HalpLibe.java | 3 + .../halplibe/util/TomlConfigHandler.java | 127 +++++++++ .../halplibe/util/toml/CommentedEntry.java | 27 ++ .../turniplabs/halplibe/util/toml/Entry.java | 27 ++ .../turniplabs/halplibe/util/toml/Toml.java | 256 ++++++++++++++++++ .../halplibe/util/toml/TomlParser.java | 51 ++++ 6 files changed, 491 insertions(+) create mode 100644 src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java create mode 100644 src/main/java/turniplabs/halplibe/util/toml/CommentedEntry.java create mode 100644 src/main/java/turniplabs/halplibe/util/toml/Entry.java create mode 100644 src/main/java/turniplabs/halplibe/util/toml/Toml.java create mode 100644 src/main/java/turniplabs/halplibe/util/toml/TomlParser.java diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index d755ab7..069743a 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -5,8 +5,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import turniplabs.halplibe.helper.*; +import turniplabs.halplibe.util.ConfigHandler; +import turniplabs.halplibe.util.TomlConfigHandler; import turniplabs.halplibe.util.achievements.AchievementPage; import turniplabs.halplibe.util.achievements.VanillaAchievementsPage; +import turniplabs.halplibe.util.toml.Toml; import java.io.File; import java.util.Properties; diff --git a/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java b/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java new file mode 100644 index 0000000..c853c51 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java @@ -0,0 +1,127 @@ +package turniplabs.halplibe.util; + +import net.fabricmc.loader.api.FabricLoader; +import turniplabs.halplibe.HalpLibe; +import turniplabs.halplibe.util.toml.Toml; +import turniplabs.halplibe.util.toml.TomlParser; + +import java.io.*; + +public class TomlConfigHandler { + private static final String CONFIG_DIRECTORY = FabricLoader.getInstance().getGameDir().toString() + "/config/"; + private final Toml defaults; + private final Toml config; + private String configFileName = ""; + + public TomlConfigHandler(String modID, Toml defaults) { + this.configFileName = modID + ".cfg"; + this.defaults = defaults; + if (defaults.getComment().isPresent()) + this.config = new Toml(defaults.getComment().get()); + else this.config = new Toml(); + + // make sure the actual config has all the required entries + config.addMissing(defaults); + + HalpLibe.LOGGER.info("Config file name: " + this.configFileName); + + File configFile = new File(getFilePath()); + HalpLibe.LOGGER.info("Config file path: " + configFile.getAbsolutePath()); + try { + if (!configFile.exists()) { + HalpLibe.LOGGER.info("Config file does not exist. Creating..."); + configFile.getParentFile().mkdirs(); + configFile.createNewFile(); + writeConfig(); + } else { + // load only reads the entries in the file + loadConfig(); + // ensure that new entries are written to the file + writeConfig(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public String getFilePath() { + return CONFIG_DIRECTORY + configFileName; + } + + /** + * equivalent to {@link TomlConfigHandler#getString(String)} + * @param key the key of the property + * @return the property's value, as a string + */ + @Deprecated + public String getProperty(String key) { + return getString(key); + } + + public String getString(String key) { + Object o = this.config.get(key); + if (o == null) return null; + return o.toString(); + } + + public int getInt(String key) { + return this.config.get(key, Integer.class); + } + public long getLong(String key) { + return this.config.get(key, Long.class); + } + + public float getFloat(String key) { + return this.config.get(key, Float.class); + } + + public double getDouble(String key) { + return this.config.get(key, Double.class); + } + + public boolean getBoolean(String key) { + return this.config.get(key, Boolean.class); + } + + public void writeConfig() { + File configFile = new File(getFilePath()); + + // make sure the actual config has all the required entries + config.merge(defaults); + + // write the config + try (OutputStream output = new FileOutputStream(configFile)) { + output.write(config.toString().getBytes()); + output.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void loadConfig() { + File configFile = new File(getFilePath()); + // make sure the actual config has all the required entries + config.merge(defaults); + loadConfig(configFile, this.config); + } + + private static void loadConfig(File configFile, Toml properties) { + try (InputStream input = new FileInputStream(configFile)) { + // only loads the ones that it finds in the file + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while (true) { + byte[] buf = new byte[Math.max(2048, input.available())]; + int count = input.read(buf); + if (count == -1) break; + baos.write(buf, 0, count); + } + + Toml parsed = TomlParser.parse(baos.toString()); + properties.merge(true, parsed); + + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/turniplabs/halplibe/util/toml/CommentedEntry.java b/src/main/java/turniplabs/halplibe/util/toml/CommentedEntry.java new file mode 100644 index 0000000..8048a01 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/toml/CommentedEntry.java @@ -0,0 +1,27 @@ +package turniplabs.halplibe.util.toml; + +public class CommentedEntry extends Entry { + String comment; + + public String getComment() { + return comment; + } + + public CommentedEntry(String comment, T t) { + super(t); + this.comment = comment; + } + + public T getT() { + return t; + } + + @Override + public String toString(String key) { + StringBuilder res = new StringBuilder(); + for (String s : comment.split("\n")) + res.append("# ").append(s).append("\n"); + res.append(super.toString(key)); + return res.toString(); + } +} diff --git a/src/main/java/turniplabs/halplibe/util/toml/Entry.java b/src/main/java/turniplabs/halplibe/util/toml/Entry.java new file mode 100644 index 0000000..e92aeea --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/toml/Entry.java @@ -0,0 +1,27 @@ +package turniplabs.halplibe.util.toml; + +public class Entry { + T t; + boolean string = false; + + public Entry(T t) { + this.t = t; + string = t instanceof String; + } + + public T getT() { + return t; + } + + public String toString(String key) { + return key + " = " + this; + } + + @Override + public String toString() { + if (string) { + return "\"" + t.toString() + "\""; + } + return t.toString(); + } +} diff --git a/src/main/java/turniplabs/halplibe/util/toml/Toml.java b/src/main/java/turniplabs/halplibe/util/toml/Toml.java new file mode 100644 index 0000000..9bcd5bc --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/toml/Toml.java @@ -0,0 +1,256 @@ +package turniplabs.halplibe.util.toml; + +import org.spongepowered.include.com.google.common.collect.ImmutableList; + +import java.util.*; + +public class Toml { + protected HashMap categories = new HashMap<>(); + protected HashMap> entries = new HashMap<>(); + protected Optional comment = Optional.empty(); + + ArrayList orderedKeys = new ArrayList<>(); + ImmutableList immutKeys = null; + + public Object get(String key) { + return get(key, Object.class); + } + + @SuppressWarnings({"unchecked", "unused", "DuplicateExpressions"}) + public T get(String key, Class clazz) { + if (key.startsWith(".")) { + if (key.substring(1).contains(".")) { + String[] split = key.substring(1).split("\\.", 2); + return (T) categories.get(split[0]).get("." + split[1]); + } + return (T) categories.get(key.substring(1)); + } else if (key.contains(".")) { + String[] split = key.split("\\.", 2); + return (T) categories.get(split[0]).get(split[1]); + } else { + Entry entry = entries.get(key); + if (entry == null) return null; + + Object value = entry.getT(); + + if (value instanceof String) { + if (clazz.equals(Byte.class) || clazz.equals(byte.class)) + return (T) (Byte) Byte.parseByte((String) value); + if (clazz.equals(Short.class) || clazz.equals(short.class)) + return (T) (Short) Short.parseShort((String) value); + if (clazz.equals(Integer.class) || clazz.equals(int.class)) + return (T) (Integer) Integer.parseInt((String) value); + if (clazz.equals(Long.class) || clazz.equals(long.class)) + return (T) (Long) Long.parseLong((String) value); + + if (clazz.equals(Float.class) || clazz.equals(float.class)) + return (T) (Float) Float.parseFloat((String) value); + if (clazz.equals(Double.class) || clazz.equals(double.class)) + return (T) (Double) Double.parseDouble((String) value); + + if (clazz.equals(Boolean.class) || clazz.equals(boolean.class)) + return (T) (Boolean) Boolean.parseBoolean((String) value); + } else { + if (clazz.equals(Float.class) || clazz.equals(float.class)) + return (T) (Float) ((Number) value).floatValue(); + if (clazz.equals(Double.class) || clazz.equals(double.class)) + return (T) (Double) ((Number) value).doubleValue(); + if (clazz.equals(Long.class) || clazz.equals(long.class)) + return (T) (Long) (((Number) value).longValue()); + if (clazz.equals(Integer.class) || clazz.equals(int.class)) + return (T) (Integer) (((Number) value).intValue()); + if (clazz.equals(Short.class) || clazz.equals(short.class)) + return (T) (Short) (((Number) value).shortValue()); + if (clazz.equals(Byte.class) || clazz.equals(byte.class)) + return (T) (Byte) (((Number) value).byteValue()); + } + + return (T) value; + } + } + + public Entry getEntry(String key) { + if (key.startsWith(".")) { + throw new RuntimeException("Categories are not entries"); + } else { + return entries.get(key); + } + } + + public Toml() { + } + + public Toml(String comment) { + this.comment = Optional.of(comment); + } + + public Toml addCategory(String comment, String name) { + return addCategory(name, new Toml(comment)); + } + + public Toml addCategory(String name) { + return addCategory(name, new Toml()); + } + + protected Toml addCategory(String name, Toml category) { + if (name.contains(".")) { + String[] split = name.split("\\.", 2); + Toml toml = categories.get(split[0]); + if (toml == null) { + categories.put(split[0], toml = new Toml()); + orderedKeys.add("." + split[0]); + } + toml.addCategory(split[1], category); + } else { + orderedKeys.add("." + name); + categories.put(name, category); + } + return category; + } + + public Toml addEntry(String name, T value) { + return addEntry(name, new Entry<>(value)); + } + + public Toml addEntry(String name, String comment, T value) { + return addEntry(name, new CommentedEntry<>(comment, value)); + } + + public Toml addEntry(String name, Entry value) { + immutKeys = null; + + if (name.contains(".")) { + String[] split = name.split("\\.", 2); + Toml toml = categories.get(split[0]); + if (toml == null) { + categories.put(split[0], toml = new Toml()); + orderedKeys.add("." + split[0]); + } + toml.addEntry(split[1], value); + } else if (entries.put(name, value) == null) + orderedKeys.add(name); + return this; + } + + public ImmutableList getOrderedKeys() { + if (immutKeys == null) + // java generics are stupid... + immutKeys = (ImmutableList) (ImmutableList) ImmutableList.builder().addAll(orderedKeys).build(); + return immutKeys; + } + + protected String repeat(String txt, int count) { + StringBuilder dst = new StringBuilder(); + for (int i = 0; i < count; i++) dst.append(txt); + return dst.toString(); + } + + public String toString(String rootKey, int indents) { + StringBuilder builder = new StringBuilder(); + + if (rootKey.isEmpty()) { + if (this.comment.isPresent()) { + String comment = this.comment.get(); + + for (String re : comment.split("\n")) + builder.append(repeat("\t", indents)).append("# ").append(re).append("\n"); + } + + builder.append("\n"); + } + + for (String orderedKey : getOrderedKeys()) { + String[] res; + int offset = 0; + int sep = 0; + + if (orderedKey.startsWith(".")) { + if (orderedKey.substring(1).contains(".")) continue; + + Toml cat = categories.get(orderedKey.substring(1)); + String full = rootKey + (rootKey.isEmpty() ? "" : ".") + orderedKey.substring(1); + + if (cat.comment.isPresent()) { + String comment = cat.comment.get(); + + for (String re : comment.split("\n")) + builder.append(repeat("\t", indents)).append("# ").append(re).append("\n"); + } + + builder.append(repeat("\t", indents)).append("[").append(full).append("]").append("\n"); + + res = cat.toString(full, 0).split("\n"); + sep = offset = 1; + } else { + res = entries.get(orderedKey).toString(orderedKey).split("\n"); + } + + for (String re : res) builder.append(repeat("\t", indents + offset)).append(re).append("\n"); + builder.append(repeat("\n", sep)); + } + + return builder.toString(); + } + + @Override + public String toString() { + return toString("", 0); + } + + public boolean contains(String entry) { + return get(entry) != null; + } + + public Optional getComment() { + return comment; + } + + public void merge(boolean complete, Toml parsed) { + for (String orderedKey : parsed.orderedKeys) { + if (orderedKey.startsWith(".")) { + orderedKey = orderedKey.substring(1); + categories.get(orderedKey).merge(complete, parsed.categories.get(orderedKey)); + } else { + if (complete) { + if (entries.containsKey(orderedKey)) { + Entry entry = entries.get(orderedKey); + if (entry instanceof CommentedEntry) { + entries.replace(orderedKey, new CommentedEntry<>( + ((CommentedEntry) entry).getComment(), + parsed.getEntry(orderedKey).t + )); + } else { + entries.replace(orderedKey, parsed.getEntry(orderedKey)); + } + } else { + entries.replace(orderedKey, parsed.getEntry(orderedKey)); + } + } else if (!this.entries.containsKey(orderedKey)) { + entries.replace(orderedKey, parsed.getEntry(orderedKey)); + } + } + } + } + + public void merge(Toml parsed) { + merge(false, parsed); + } + + public void addMissing(Toml other) { + for (String orderedKey : other.getOrderedKeys()) { + if (orderedKey.startsWith(".")) { + if (!this.categories.containsKey(orderedKey)) { + Toml toml = other.get(orderedKey, Toml.class); + if (toml.getComment().isPresent()) + addCategory(toml.getComment().get(), orderedKey.substring(1)); + else addCategory(orderedKey.substring(1)); + categories.get(orderedKey.substring(1)).addMissing(toml); + } + } else { + if (!this.entries.containsKey(orderedKey)) { + addEntry(orderedKey, other.getEntry(orderedKey)); + } + } + } + } +} diff --git a/src/main/java/turniplabs/halplibe/util/toml/TomlParser.java b/src/main/java/turniplabs/halplibe/util/toml/TomlParser.java new file mode 100644 index 0000000..ebd2d9f --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/toml/TomlParser.java @@ -0,0 +1,51 @@ +package turniplabs.halplibe.util.toml; + +public class TomlParser { + public static Toml parse(String src) { + Toml toml1 = new Toml(); + + String cat = ""; + for (String s : src.split("\n")) { + s = s.trim(); + + if (s.startsWith("[") && s.endsWith("]")) { + while (s.startsWith("[") && s.endsWith("]")) { + s = s.substring(1, s.length() - 1); + cat = s; + } + cat = cat.trim(); + continue; + } + + if (s.startsWith("#") || s.isEmpty()) continue; + + String[] split = s.split("=", 2); + String key = cat + (cat.isEmpty() ? "" : ".") + split[0].trim(); + String value = split[1].trim(); + + if (value.startsWith("\"")) { + toml1.addEntry(key, new Entry<>(value.substring(1, value.length() - 1))); + } else if (value.contains(".")) { + toml1.addEntry(key, new Entry<>(Double.parseDouble(value))); + } else { + if (value.equals("true")) + toml1.addEntry(key, new Entry<>(true)); + else if (value.equals("false")) + toml1.addEntry(key, new Entry<>(false)); + else { + try { + toml1.addEntry(key, new Entry<>(Integer.parseInt(value))); + } catch (Throwable err) { + try { + toml1.addEntry(key, new Entry<>(Double.parseDouble(value))); + } catch (Throwable ignored1) { + toml1.addEntry(key, new Entry<>(value)); + } + } + } + } + } + + return toml1; + } +} From ffd939061215b760c512efcc0a0f089337e9783e Mon Sep 17 00:00:00 2001 From: GiantLuigi4 <49770992+GiantLuigi4@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:33:46 -0400 Subject: [PATCH 2/4] forgot to remove excess imports --- src/main/java/turniplabs/halplibe/HalpLibe.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index 069743a..d755ab7 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -5,11 +5,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import turniplabs.halplibe.helper.*; -import turniplabs.halplibe.util.ConfigHandler; -import turniplabs.halplibe.util.TomlConfigHandler; import turniplabs.halplibe.util.achievements.AchievementPage; import turniplabs.halplibe.util.achievements.VanillaAchievementsPage; -import turniplabs.halplibe.util.toml.Toml; import java.io.File; import java.util.Properties; From 4b0acec4c3e18a882853c1806027fdb923461a23 Mon Sep 17 00:00:00 2001 From: GiantLuigi4 <49770992+GiantLuigi4@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:35:40 -0400 Subject: [PATCH 3/4] oh yea --- src/main/java/turniplabs/halplibe/util/ConfigHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/turniplabs/halplibe/util/ConfigHandler.java b/src/main/java/turniplabs/halplibe/util/ConfigHandler.java index d6c19b8..17ba110 100644 --- a/src/main/java/turniplabs/halplibe/util/ConfigHandler.java +++ b/src/main/java/turniplabs/halplibe/util/ConfigHandler.java @@ -7,7 +7,7 @@ import java.util.Properties; public class ConfigHandler { - private static final String CONFIG_DIRECTORY = FabricLoader.getInstance().getConfigDir().toString() + "/config/"; + private static final String CONFIG_DIRECTORY = FabricLoader.getInstance().getGameDir().toString() + "/config/"; private final Properties defaultProperties; private final Properties properties; private String configFileName = ""; From 8e6693fdf847a288e37bdd46c52197f5f3ea697b Mon Sep 17 00:00:00 2001 From: GiantLuigi4 <49770992+GiantLuigi4@users.noreply.github.com> Date: Sun, 29 Oct 2023 23:04:56 -0400 Subject: [PATCH 4/4] updater --- .../halplibe/util/ConfigUpdater.java | 45 +++++++++++++++++++ .../halplibe/util/TomlConfigHandler.java | 22 ++++----- .../turniplabs/halplibe/util/toml/Toml.java | 20 +++++++++ 3 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 src/main/java/turniplabs/halplibe/util/ConfigUpdater.java diff --git a/src/main/java/turniplabs/halplibe/util/ConfigUpdater.java b/src/main/java/turniplabs/halplibe/util/ConfigUpdater.java new file mode 100644 index 0000000..e95f463 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/ConfigUpdater.java @@ -0,0 +1,45 @@ +package turniplabs.halplibe.util; + +import turniplabs.halplibe.util.toml.Toml; + +import java.util.HashMap; + +/** + * Mainly + */ +public abstract class ConfigUpdater { + Toml updating; + + public static ConfigUpdater fromProperties( + String... text + ) { + TomlConverter converter = new TomlConverter(); + for (int i = 0; i < text.length; i += 2) { + converter.conversions.put( + text[i], text[i + 1] + ); + } + return converter; + } + + public abstract void update(); + + private static class TomlConverter extends ConfigUpdater { + HashMap conversions = new HashMap<>(); + + @Override + public void update() { + // if it's a toml, then there's no point in updating + for (String orderedKey : updating.getOrderedKeys()) { + if (orderedKey.startsWith(".")) + return; + } + + for (String s : conversions.keySet()) { + String str = updating.get(s).toString(); + updating.remove(s); + updating.addEntry(conversions.get(s), str); + } + } + } +} diff --git a/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java b/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java index c853c51..6731262 100644 --- a/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java +++ b/src/main/java/turniplabs/halplibe/util/TomlConfigHandler.java @@ -13,7 +13,14 @@ public class TomlConfigHandler { private final Toml config; private String configFileName = ""; + private ConfigUpdater updater; + public TomlConfigHandler(String modID, Toml defaults) { + this(null, modID, defaults); + } + + public TomlConfigHandler(ConfigUpdater updater, String modID, Toml defaults) { + this.updater = updater; this.configFileName = modID + ".cfg"; this.defaults = defaults; if (defaults.getComment().isPresent()) @@ -48,16 +55,6 @@ public String getFilePath() { return CONFIG_DIRECTORY + configFileName; } - /** - * equivalent to {@link TomlConfigHandler#getString(String)} - * @param key the key of the property - * @return the property's value, as a string - */ - @Deprecated - public String getProperty(String key) { - return getString(key); - } - public String getString(String key) { Object o = this.config.get(key); if (o == null) return null; @@ -67,6 +64,7 @@ public String getString(String key) { public int getInt(String key) { return this.config.get(key, Integer.class); } + public long getLong(String key) { return this.config.get(key, Long.class); } @@ -105,7 +103,7 @@ public void loadConfig() { loadConfig(configFile, this.config); } - private static void loadConfig(File configFile, Toml properties) { + private void loadConfig(File configFile, Toml properties) { try (InputStream input = new FileInputStream(configFile)) { // only loads the ones that it finds in the file ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -117,6 +115,8 @@ private static void loadConfig(File configFile, Toml properties) { } Toml parsed = TomlParser.parse(baos.toString()); + updater.updating = parsed; + updater.update(); properties.merge(true, parsed); input.close(); diff --git a/src/main/java/turniplabs/halplibe/util/toml/Toml.java b/src/main/java/turniplabs/halplibe/util/toml/Toml.java index 9bcd5bc..ee341dd 100644 --- a/src/main/java/turniplabs/halplibe/util/toml/Toml.java +++ b/src/main/java/turniplabs/halplibe/util/toml/Toml.java @@ -253,4 +253,24 @@ public void addMissing(Toml other) { } } } + + public void remove(String s) { + if (s.startsWith(".")) { + if (s.substring(1).contains(".")) { + categories.get(s.substring(1).split("\\.")[0]) + .remove("." + s.substring(1).split("\\.")[1]); + return; + } + orderedKeys.remove(s); + categories.remove(s.substring(1)); + } else { + if (s.contains(".")) { + categories.get(s.split("\\.")[0]) + .remove(s.split("\\.")[1]); + return; + } + orderedKeys.remove(s); + entries.remove(s); + } + } }