diff --git a/src/main/java/org/mineacademy/fo/ChatUtil.java b/src/main/java/org/mineacademy/fo/ChatUtil.java
index 64b9f1983..8660f8c7b 100644
--- a/src/main/java/org/mineacademy/fo/ChatUtil.java
+++ b/src/main/java/org/mineacademy/fo/ChatUtil.java
@@ -1,6 +1,14 @@
package org.mineacademy.fo;
-import java.awt.Color;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.bukkit.ChatColor;
+import org.mineacademy.fo.MinecraftVersion.V;
+import org.mineacademy.fo.model.Whiteblacklist;
+import org.mineacademy.fo.plugin.SimplePlugin;
+import org.mineacademy.fo.remain.CompChatColor;
+
+import java.awt.*;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -9,15 +17,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.bukkit.ChatColor;
-import org.mineacademy.fo.MinecraftVersion.V;
-import org.mineacademy.fo.model.Whiteblacklist;
-import org.mineacademy.fo.plugin.SimplePlugin;
-import org.mineacademy.fo.remain.CompChatColor;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
/**
* Utility class for managing in-game chat.
*/
@@ -225,8 +224,7 @@ public static String capitalizeFirst(final String message) {
* capitalizeFully("") = ""
* capitalizeFully("i am FINE") = "I Am Fine"
*
- *
- * @param message the String to capitalize, may be null
+
* @return capitalized String, null
if null String input
*/
public static String capitalizeFully(String str) {
@@ -262,7 +260,6 @@ private static String capitalizeFully(String str, char[] delimiters) {
*
*
* @author Apache Commons - WordUtils
- * @param message the String to capitalize, may be null
* @return capitalized String, null
if null String input
*/
public static String capitalize(String str) {
@@ -299,6 +296,34 @@ private static String capitalize(String str, char[] delimiters) {
return str;
}
+ /**
+ * Converts UPPER_UNDERSCORE string to Capital_Underscore
+ */
+ public static String upperToCapitalUnderscore(String s){
+ char[] array = s.toLowerCase().toCharArray();
+ StringBuilder sb = new StringBuilder();
+ boolean capitalizeNext = false;
+ char next;
+
+ sb.append(Character.toUpperCase(array[0]));
+ for (int i = 1; i < array.length; i++){
+ if (array[i] == '_'){
+ capitalizeNext = true;
+ next = array[i];
+ } else {
+ if (capitalizeNext){
+ next = Character.toUpperCase(array[i]);
+ capitalizeNext = false;
+ }
+ else{
+ next = array[i];
+ }
+ }
+ sb.append(next);
+ }
+ return sb.toString();
+ }
+
private static boolean isDelimiter(char ch, char[] delimiters) {
if (delimiters == null)
return Character.isWhitespace(ch);
diff --git a/src/main/java/org/mineacademy/fo/annotation/AutoConfig.java b/src/main/java/org/mineacademy/fo/annotation/AutoConfig.java
index b9ac015eb..63b21da08 100644
--- a/src/main/java/org/mineacademy/fo/annotation/AutoConfig.java
+++ b/src/main/java/org/mineacademy/fo/annotation/AutoConfig.java
@@ -8,7 +8,8 @@
import java.lang.annotation.Target;
/**
- * This annotation automatically saves and loads fields between a file and a YamlConfig.
+ * This annotation automatically saves and loads fields between a file and your custom
+ * class extending {@link org.mineacademy.fo.settings.YamlConfig}.
* On class:
*
* See {@link SimpleSettings#LOCALE_PREFIX} for the locale prefix. *
* The locale file is extracted from your plugins jar to the localization/ folder - * if it does not exists, or updated if it is out of date. + * if it does not exist, or updated if it is out of date. */ @Override protected final void onLoad() throws Exception { - final String localePath = "localization/messages_" + SimpleSettings.LOCALE_PREFIX + ".yml"; + final String localePath = getFilePrefix() + SimpleSettings.LOCALE_PREFIX + ".yml"; final Object content = FileUtil.getInternalFileContent(localePath); - Valid.checkNotNull(content, SimplePlugin.getNamed() + " does not support the localization: messages_" + SimpleSettings.LOCALE_PREFIX + Valid.checkNotNull(content, SimplePlugin.getNamed() + " does not support the localization: " + getFilePrefix() + SimpleSettings.LOCALE_PREFIX + ".yml (For custom locale, set the Locale to 'en' and edit your English file instead)"); this.loadConfiguration(localePath); @@ -103,8 +114,8 @@ protected final boolean alwaysSaveOnLoad() { public static final class Commands { /** - * true = https://i.imgur.com/us88BCT.png - * false = https://i.imgur.com/N7jLu7v.png + * true = ... + * false = ... */ public static Boolean SIMPLE_HELP_DESIGN = false; @@ -741,7 +752,7 @@ private static void init() { * * @return */ - public static final Boolean isLocalizationCalled() { + public static Boolean isLocalizationCalled() { return localizationClassCalled; } @@ -749,7 +760,7 @@ public static final Boolean isLocalizationCalled() { * Reset the flag indicating that the class has been loaded, * used in reloading. */ - public static final void resetLocalizationCall() { + public static void resetLocalizationCall() { localizationClassCalled = false; } } diff --git a/src/main/java/org/mineacademy/fo/settings/SimpleSettings.java b/src/main/java/org/mineacademy/fo/settings/SimpleSettings.java index 9cb8ead5a..78a76b009 100644 --- a/src/main/java/org/mineacademy/fo/settings/SimpleSettings.java +++ b/src/main/java/org/mineacademy/fo/settings/SimpleSettings.java @@ -15,7 +15,7 @@ * A simple implementation of a typical main plugin settings * where each key can be accessed in a static way from anywhere. *
- * Typically we use this class for settings.yml main plugin config. + * Typically, we use this class for settings.yml main plugin config. */ // Use for settings.yml @SuppressWarnings("unused") @@ -175,15 +175,14 @@ protected int getConfigVersion() { public static Boolean NOTIFY_UPDATES = true; /** - * Should we warn about incompatibilities. + * Should we warn about incompatibilities? * It is used in {@link org.mineacademy.fo.plugin.AutoRegisterScanner#autoRegister(Class, boolean)}. * Please hide these warnings only on the last stage of your plugin development. */ public static Boolean HIDE_INCOMPATIBILITY_WARNINGS = false; /** - * Should we warn about Nashorn not installed on the server. - * Is is used + * Should we warn about Nashorn not installed on the server? */ public static Boolean HIDE_NASHORN_WARNINGS = false; @@ -262,7 +261,7 @@ private static void init() { } /** - * Upgrade some of the old and ancient settings from our premium plugins. + * Upgrade some old and ancient settings from our premium plugins. */ private static void upgradeOldSettings() { diff --git a/src/main/java/org/mineacademy/fo/settings/YamlConfig.java b/src/main/java/org/mineacademy/fo/settings/YamlConfig.java index 978514bd7..54fd90958 100644 --- a/src/main/java/org/mineacademy/fo/settings/YamlConfig.java +++ b/src/main/java/org/mineacademy/fo/settings/YamlConfig.java @@ -153,6 +153,8 @@ public final void loadConfiguration(@Nullable String from, String to) { File file; boolean justCreated = !FileUtil.getFile(to).exists(); + this.onPreLoad(); + if (from != null) { // Copy if not exists yet @@ -170,16 +172,20 @@ public final void loadConfiguration(@Nullable String from, String to) { this.defaults = defaultConfig.section; this.defaultsPath = from; + + this.load(file); } else { file = FileUtil.getOrMakeFile(to); this.file = file; - } - if (!justCreated){ - this.load(file); + if (!justCreated){ + this.load(file); + } } + + this.onLoadFinish(); } /** @@ -223,7 +229,6 @@ public final void loadInternal(String internalPath) { /* * Dumps all values in this config into a saveable format */ - @NonNull @Override final String saveToString() { diff --git a/src/main/java/org/mineacademy/fo/settings/YamlStaticConfig.java b/src/main/java/org/mineacademy/fo/settings/YamlStaticConfig.java index 7e31dfa02..8b53424aa 100644 --- a/src/main/java/org/mineacademy/fo/settings/YamlStaticConfig.java +++ b/src/main/java/org/mineacademy/fo/settings/YamlStaticConfig.java @@ -1,8 +1,11 @@ package org.mineacademy.fo.settings; +import com.google.common.base.CaseFormat; import org.bukkit.Material; +import org.mineacademy.fo.ChatUtil; import org.mineacademy.fo.Common; import org.mineacademy.fo.Valid; +import org.mineacademy.fo.annotation.AutoStaticConfig; import org.mineacademy.fo.collection.SerializedMap; import org.mineacademy.fo.collection.StrictList; import org.mineacademy.fo.model.BoxedMessage; @@ -45,6 +48,8 @@ public abstract class YamlStaticConfig { */ private static YamlConfig TEMPORARY_INSTANCE; + private final String canonical = this.getClass().getCanonicalName(); + /** * Internal use only: Create a new {@link YamlConfig} instance and link it to load fields via * reflection. @@ -165,6 +170,11 @@ private void loadViaReflection() { try { this.preLoad(); + final AutoStaticConfig ann = this.getClass().getAnnotation(AutoStaticConfig.class); + if (ann != null && ann.value()){ + setNestedFields(this.getClass()); + } + // Parent class if applicable. if (YamlStaticConfig.class.isAssignableFrom(this.getClass().getSuperclass())) { final Class> superClass = this.getClass().getSuperclass(); @@ -183,6 +193,49 @@ private void loadViaReflection() { } } + /** + * Set all fields from the given class and from its nested classes to their correspondent values from the file. + */ + public void setNestedFields(Class> clazz){ + if (clazz.getDeclaredClasses().length > 0){ + for (Class> inner : clazz.getDeclaredClasses()){ + setNestedFields(inner); + } + } + for (Field field : clazz.getDeclaredFields()){ + AutoStaticConfig ann = field.getAnnotation(AutoStaticConfig.class); + if (!Modifier.isStatic(field.getModifiers())) continue; + if (ann != null && !ann.value()){ + continue; + } + + Class> decl = field.getDeclaringClass(); + String path = (decl.getCanonicalName() + "." + getFormattedFieldName(field)) + .replace(canonical + ".", "") + .replace(canonical, ""); + + field.setAccessible(true); + try { + field.set(this, TEMPORARY_INSTANCE.getBasedOnClass(path, field)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Get the name under which the value will be saved in the file. + * Based on user-defined AutoConfig.format() and is Capital_Underscore by default. + */ + private String getFormattedFieldName(Field field) { + String name = ChatUtil.upperToCapitalUnderscore(field.getName()); + AutoStaticConfig ann = this.getClass().getAnnotation(AutoStaticConfig.class); + if (ann != null && ann.format().length != 0){ + name = CaseFormat.UPPER_UNDERSCORE.to(ann.format()[0], field.getName()); + } + return name; + } + /* * Invoke all "private static void init()" methods in the class and its subclasses */ @@ -202,7 +255,7 @@ private void invokeMethodsIn(final Class> clazz) throws Exception { for (final Method method : clazz.getDeclaredMethods()) { - // After each invocation check if the invoication broke the plugin and ignore + // After each invocation check if the invocation broke the plugin and ignore if (!instance.isEnabled()) return;