Skip to content

Commit d21836d

Browse files
authored
Merge pull request #540 from ayyhimself/ayys-json-config
add setting and getting config in json format
2 parents 41197ab + 2488955 commit d21836d

File tree

8 files changed

+74
-90
lines changed

8 files changed

+74
-90
lines changed

src/main/java/net/discordjug/javabot/RuntimeHintsConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
6868
for(Class<?> cl : getClass().getClassLoader().loadClass("sun.font.FontConfigManager").getDeclaredClasses()) {
6969
hints.jni().registerType(cl, MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
7070
}
71-
} catch (ClassNotFoundException e) {
72-
throw new RuntimeException(e);
71+
} catch (ClassNotFoundException _) {
72+
// FontConfigManager is only supported on Linux
7373
}
7474

7575
for (Class<?> cl : WebhookEmbed.class.getClasses()) {

src/main/java/net/discordjug/javabot/data/config/BotConfig.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package net.discordjug.javabot.data.config;
22

3-
import com.google.gson.Gson;
4-
import com.google.gson.GsonBuilder;
53
import com.google.gson.JsonSyntaxException;
64
import lombok.extern.slf4j.Slf4j;
75
import net.discordjug.javabot.util.ExceptionLogger;
6+
import net.discordjug.javabot.util.GsonUtils;
87
import net.dv8tion.jda.api.entities.Guild;
98

109
import org.jetbrains.annotations.NotNull;
@@ -64,11 +63,10 @@ public BotConfig(Path dir) {
6463
}
6564
}
6665
this.guilds = new ConcurrentHashMap<>();
67-
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
6866
Path systemsFile = dir.resolve(SYSTEMS_FILE);
6967
if (Files.exists(systemsFile)) {
7068
try (BufferedReader reader = Files.newBufferedReader(systemsFile)) {
71-
this.systemsConfig = gson.fromJson(reader, SystemsConfig.class);
69+
this.systemsConfig = GsonUtils.fromJson(reader, SystemsConfig.class);
7270
log.info("Loaded systems config from {}", systemsFile);
7371
} catch (JsonSyntaxException e) {
7472
ExceptionLogger.capture(e, getClass().getSimpleName());
@@ -133,10 +131,9 @@ public SystemsConfig getSystems() {
133131
* Flushes all configuration to the disk.
134132
*/
135133
public void flush() {
136-
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
137134
Path systemsFile = this.dir.resolve(SYSTEMS_FILE);
138135
try (BufferedWriter writer = Files.newBufferedWriter(systemsFile)) {
139-
gson.toJson(this.systemsConfig, writer);
136+
GsonUtils.toJson(this.systemsConfig, writer);
140137
writer.flush();
141138
} catch (IOException e) {
142139
ExceptionLogger.capture(e, getClass().getSimpleName());

src/main/java/net/discordjug/javabot/data/config/GuildConfig.java

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package net.discordjug.javabot.data.config;
22

3-
import com.google.gson.Gson;
4-
import com.google.gson.GsonBuilder;
53
import com.google.gson.JsonSyntaxException;
64
import lombok.Data;
75
import lombok.extern.slf4j.Slf4j;
86
import net.discordjug.javabot.data.config.guild.*;
97
import net.discordjug.javabot.util.ExceptionLogger;
8+
import net.discordjug.javabot.util.GsonUtils;
109
import net.discordjug.javabot.util.Pair;
1110
import net.dv8tion.jda.api.entities.Guild;
1211

@@ -20,7 +19,6 @@
2019
import java.nio.file.Path;
2120
import java.util.List;
2221
import java.util.Optional;
23-
import java.util.regex.Pattern;
2422

2523
/**
2624
* A collection of guild-specific configuration items, each of which represents
@@ -71,13 +69,10 @@ public GuildConfig(Guild guild, Path file) {
7169
* @throws UncheckedIOException if an IO error occurs.
7270
*/
7371
public static GuildConfig loadOrCreate(Guild guild, Path file) {
74-
Gson gson = new GsonBuilder()
75-
.registerTypeAdapter(Pattern.class, new PatternTypeAdapter())
76-
.create();
7772
GuildConfig config;
7873
if (Files.exists(file)) {
7974
try (BufferedReader reader = Files.newBufferedReader(file)) {
80-
config = gson.fromJson(reader, GuildConfig.class);
75+
config = GsonUtils.fromJson(reader, GuildConfig.class);
8176
config.setFile(file);
8277
config.setGuild(guild);
8378
log.info("Loaded config from {}", file);
@@ -118,13 +113,8 @@ private void setGuild(Guild guild) {
118113
* Saves this config to its file path.
119114
*/
120115
public synchronized void flush() {
121-
Gson gson = new GsonBuilder()
122-
.serializeNulls()
123-
.setPrettyPrinting()
124-
.registerTypeAdapter(Pattern.class, new PatternTypeAdapter())
125-
.create();
126116
try (BufferedWriter writer = Files.newBufferedWriter(this.file)) {
127-
gson.toJson(this, writer);
117+
GsonUtils.toJson(this, writer);
128118
writer.flush();
129119
} catch (IOException e) {
130120
ExceptionLogger.capture(e, getClass().getSimpleName());
@@ -167,7 +157,10 @@ public void set(String propertyName, String value) throws UnknownPropertyExcepti
167157
Optional<Pair<Field, Object>> result = ReflectionUtils.resolveField(propertyName, this);
168158
result.ifPresent(pair -> {
169159
try {
170-
ReflectionUtils.set(pair.first(), pair.second(), value);
160+
Object updatedField = ReflectionUtils.set(pair.first(), pair.second(), value);
161+
if (updatedField instanceof GuildConfigItem item) {
162+
item.setGuildConfig(this);
163+
}
171164
this.flush();
172165
} catch (IllegalAccessException e) {
173166
ExceptionLogger.capture(e, getClass().getSimpleName());

src/main/java/net/discordjug/javabot/data/config/ReflectionUtils.java

Lines changed: 7 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.extern.slf4j.Slf4j;
44
import net.discordjug.javabot.util.ExceptionLogger;
5+
import net.discordjug.javabot.util.GsonUtils;
56
import net.discordjug.javabot.util.Pair;
67

78
import org.jetbrains.annotations.NotNull;
@@ -10,31 +11,13 @@
1011
import java.lang.reflect.Field;
1112
import java.lang.reflect.Modifier;
1213
import java.util.Arrays;
13-
import java.util.HashMap;
14-
import java.util.Map;
1514
import java.util.Optional;
16-
import java.util.function.Function;
1715

1816
/**
1917
* Utility class for resolving JSON files.
2018
*/
2119
@Slf4j
2220
public class ReflectionUtils {
23-
private static final Map<Class<?>, Function<String, Object>> propertyTypeParsers = new HashMap<>();
24-
25-
static {
26-
propertyTypeParsers.put(Integer.class, Integer::parseInt);
27-
propertyTypeParsers.put(int.class, Integer::parseInt);
28-
propertyTypeParsers.put(Long.class, Long::parseLong);
29-
propertyTypeParsers.put(long.class, Long::parseLong);
30-
propertyTypeParsers.put(Float.class, Float::parseFloat);
31-
propertyTypeParsers.put(float.class, Float::parseFloat);
32-
propertyTypeParsers.put(Double.class, Double::parseDouble);
33-
propertyTypeParsers.put(double.class, Double::parseDouble);
34-
propertyTypeParsers.put(Boolean.class, Boolean::parseBoolean);
35-
propertyTypeParsers.put(String.class, s -> s);
36-
}
37-
3821
private ReflectionUtils() {
3922
}
4023

@@ -92,47 +75,17 @@ private static Field findDeclaredField(Class<?> cl, String name) throws NoSuchFi
9275
}
9376

9477
/**
95-
* Gets a mapping of properties and their type, recursively for the given
96-
* type.
97-
*
98-
* @param parentPropertyName The root property name to append child field
99-
* names to. This is null for the base case.
100-
* @param parentClass The class to search for properties in.
101-
* @return The map of properties and their types.
102-
* @throws IllegalAccessException If a field cannot have its value obtained.
103-
*/
104-
public static @NotNull Map<String, Class<?>> getFields(@NotNull String parentPropertyName, @NotNull Class<?> parentClass) throws IllegalAccessException {
105-
Map<String, Class<?>> fieldsMap = new HashMap<>();
106-
for (Field field : parentClass.getDeclaredFields()) {
107-
// Skip transient fields.
108-
if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) continue;
109-
field.setAccessible(true);
110-
String fieldPropertyName = parentPropertyName == null ? field.getName() : parentPropertyName + "." + field.getName();
111-
// Check if the field represents a "leaf" property, one which does not have any children.
112-
if (propertyTypeParsers.containsKey(field.getType())) {
113-
fieldsMap.put(fieldPropertyName, field.getType());
114-
} else {
115-
Map<String, Class<?>> childFieldsMap = getFields(fieldPropertyName, field.getType());
116-
fieldsMap.putAll(childFieldsMap);
117-
}
118-
}
119-
return fieldsMap;
120-
}
121-
122-
/**
123-
* Sets the value of a field to a certain value, using {@link ReflectionUtils#propertyTypeParsers}
124-
* to try and parse the correct value.
78+
* Sets the value of a field to a new value.
12579
*
12680
* @param field The field to set.
12781
* @param parent The object whose property value to set.
12882
* @param s The string representation of the value.
83+
* @return Returns the new value.
12984
* @throws IllegalAccessException If the field cannot be set.
13085
*/
131-
public static void set(@NotNull Field field, @NotNull Object parent, @NotNull String s) throws IllegalAccessException {
132-
Function<String, Object> parser = propertyTypeParsers.get(field.getType());
133-
if (parser == null) {
134-
throw new IllegalArgumentException("No supported property type parser for the type " + field.getType().getSimpleName());
135-
}
136-
field.set(parent, parser.apply(s));
86+
public static Object set(@NotNull Field field, @NotNull Object parent, @NotNull String s) throws IllegalAccessException {
87+
Object value = field.getType() == String.class ? s : GsonUtils.fromJson(s, field.getGenericType());
88+
field.set(parent, value);
89+
return value;
13790
}
13891
}

src/main/java/net/discordjug/javabot/listener/filter/MessageRuleFilter.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,18 @@
1111
import java.time.Duration;
1212
import java.util.Base64;
1313
import java.util.List;
14-
import java.util.regex.Pattern;
1514
import java.util.stream.Collectors;
1615

17-
import com.google.gson.Gson;
18-
import com.google.gson.GsonBuilder;
1916
import lombok.RequiredArgsConstructor;
2017
import net.discordjug.javabot.data.config.BotConfig;
21-
import net.discordjug.javabot.data.config.PatternTypeAdapter;
2218
import net.discordjug.javabot.data.config.guild.MessageRule;
2319
import net.discordjug.javabot.data.config.guild.MessageRule.MessageAction;
2420
import net.discordjug.javabot.data.config.guild.ModerationConfig;
2521
import net.discordjug.javabot.data.h2db.message_cache.MessageCache;
2622
import net.discordjug.javabot.data.h2db.message_cache.model.CachedMessage;
2723
import net.discordjug.javabot.util.Checks;
2824
import net.discordjug.javabot.util.ExceptionLogger;
25+
import net.discordjug.javabot.util.GsonUtils;
2926
import net.dv8tion.jda.api.EmbedBuilder;
3027
import net.dv8tion.jda.api.entities.Message.Attachment;
3128
import net.dv8tion.jda.api.entities.Message;
@@ -73,17 +70,12 @@ public MessageModificationStatus processMessage(MessageContent content) {
7370
}
7471

7572
private void log(MessageContent content, MessageRule ruleToExecute, ModerationConfig moderationConfig) {
76-
Gson gson = new GsonBuilder()
77-
.serializeNulls()
78-
.setPrettyPrinting()
79-
.registerTypeAdapter(Pattern.class, new PatternTypeAdapter())
80-
.create();
8173
EmbedBuilder embed = messageCache.buildMessageCacheEmbed(
8274
content.event().getMessage().getChannel(),
8375
content.event().getMessage().getAuthor(),
8476
CachedMessage.of(content.event().getMessage()), "Message content")
8577
.setTitle("Message rule triggered")
86-
.addField("Rule description", "```\n" + gson.toJson(ruleToExecute) + "\n```", false);
78+
.addField("Rule description", "```\n" + GsonUtils.toJson(ruleToExecute) + "\n```", false);
8779
if (!content.attachments().isEmpty()) {
8880
embed.addField("Attachment hashes", computeAttachmentDescription(content.attachments()), false);
8981
}

src/main/java/net/discordjug/javabot/systems/configuration/GetConfigSubcommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import net.discordjug.javabot.data.config.BotConfig;
44
import net.discordjug.javabot.data.config.GuildConfig;
55
import net.discordjug.javabot.data.config.UnknownPropertyException;
6+
import net.discordjug.javabot.util.GsonUtils;
67
import net.discordjug.javabot.util.Responses;
78
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
89
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
@@ -38,7 +39,8 @@ public ReplyCallbackAction handleConfigSubcommand(@Nonnull SlashCommandInteracti
3839
}
3940
String property = propertyOption.getAsString().trim();
4041
Object value = config.resolve(property);
41-
return Responses.info(event, "Configuration Property", "The value of the property `%s` is:\n```\n%s\n```", property, value);
42+
String json = GsonUtils.toJson(value);
43+
return Responses.info(event, "Configuration Property", "The value of the property `%s` is:\n```\n%s\n```", property, json);
4244
}
4345

4446
@Override

src/main/java/net/discordjug/javabot/systems/configuration/SetConfigSubcommand.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package net.discordjug.javabot.systems.configuration;
22

3+
import com.google.gson.JsonSyntaxException;
34
import net.discordjug.javabot.annotations.AutoDetectableComponentHandler;
45
import net.discordjug.javabot.data.config.BotConfig;
56
import net.discordjug.javabot.data.config.GuildConfig;
67
import net.discordjug.javabot.data.config.UnknownPropertyException;
8+
import net.discordjug.javabot.util.GsonUtils;
79
import net.discordjug.javabot.util.Responses;
810
import net.dv8tion.jda.api.components.label.Label;
911
import net.dv8tion.jda.api.components.textinput.TextInput;
@@ -57,10 +59,16 @@ public InteractionCallbackAction<?> handleConfigSubcommand(@Nonnull SlashCommand
5759
if (resolved == null) {
5860
return Responses.error(event, "Config `%s` not found", property);
5961
}
62+
String value;
63+
if (resolved instanceof String s) {
64+
value = s;
65+
} else {
66+
value = GsonUtils.toJson(resolved);
67+
}
6068
return event.replyModal(
6169
Modal.create(ComponentIdBuilder.build("config-set", property), "Change configuration value")
6270
.addComponents(Label.of("new value", TextInput.create("value", TextInputStyle.PARAGRAPH)
63-
.setValue(String.valueOf(resolved))
71+
.setValue(value)
6472
.build()))
6573
.build());
6674
}
@@ -78,8 +86,8 @@ public void handleModal(ModalInteractionEvent event, List<ModalMapping> values)
7886
try {
7987
guildConfig.set(property, valueString);
8088
Responses.success(event, "Configuration Updated", "The property `%s` has been set to `%s`.", property, valueString).queue();
81-
} catch (UnknownPropertyException e) {
82-
Responses.error(event, "Property not found: %s", property).queue();
89+
} catch (UnknownPropertyException | JsonSyntaxException e) {
90+
Responses.error(event, "Error while setting new value").queue();
8391
}
8492
}
8593

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package net.discordjug.javabot.util;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.GsonBuilder;
5+
import net.discordjug.javabot.data.config.PatternTypeAdapter;
6+
7+
import java.io.Reader;
8+
import java.io.Writer;
9+
import java.lang.reflect.Type;
10+
import java.util.regex.Pattern;
11+
12+
/**
13+
* Utility class that contains several methods for using Gson.
14+
*/
15+
public class GsonUtils {
16+
17+
private static final Gson gson = new GsonBuilder()
18+
.serializeNulls()
19+
.setPrettyPrinting()
20+
.registerTypeAdapter(Pattern.class, new PatternTypeAdapter())
21+
.enableComplexMapKeySerialization()
22+
.create();
23+
24+
public static <T> T fromJson(Reader json, Class<T> type) {
25+
return gson.fromJson(json, type);
26+
}
27+
28+
public static Object fromJson(String json, Type type) {
29+
return gson.fromJson(json, type);
30+
}
31+
32+
public static String toJson(Object object) {
33+
return gson.toJson(object);
34+
}
35+
36+
public static void toJson(Object object, Writer writer) {
37+
gson.toJson(object, writer);
38+
}
39+
}

0 commit comments

Comments
 (0)