66import com .cyr1en .commandprompter .config .annotations .type .ConfigHeader ;
77import com .cyr1en .commandprompter .config .annotations .type .ConfigPath ;
88import com .cyr1en .commandprompter .config .annotations .type .Configuration ;
9+ import com .cyr1en .commandprompter .config .handlers .ConfigTypeHandlerFactory ;
910import com .cyr1en .kiso .mc .configuration .base .Config ;
1011import com .cyr1en .kiso .mc .configuration .base .ConfigManager ;
1112
1213import java .lang .reflect .Field ;
1314import java .lang .reflect .InvocationTargetException ;
1415import java .util .ArrayList ;
15- import java .util .Arrays ;
16- import java .util .List ;
1716import java .util .regex .Pattern ;
1817
1918/**
@@ -36,11 +35,23 @@ public class ConfigurationManager {
3635 private final ConfigManager configManager ;
3736 private final PluginLogger logger ;
3837
38+ /**
39+ * Constructs a new ConfigurationManager for a given plugin.
40+ *
41+ * @param plugin The plugin instance which this manager will handle configurations for.
42+ */
3943 public ConfigurationManager (CommandPrompter plugin ) {
4044 this .configManager = new ConfigManager (plugin );
4145 this .logger = plugin .getPluginLogger ();
4246 }
4347
48+ /**
49+ * Retrieves or initializes the configuration for a specified configuration class.
50+ *
51+ * @param <T> The type of the configuration record.
52+ * @param configClass The class of the configuration record.
53+ * @return An instance of T with values from the configuration, or null if configuration class is not annotated properly.
54+ */
4455 public <T > T getConfig (Class <T > configClass ) {
4556 if (configClass .getAnnotation (Configuration .class ) == null )
4657 return null ;
@@ -52,39 +63,30 @@ public <T> T getConfig(Class<T> configClass) {
5263 var configValues = new ArrayList <>();
5364 configValues .add (config );
5465
55- for (Field declaredField : configClass .getDeclaredFields ()) {
56- if (declaredField .getAnnotation (ConfigNode .class ) == null ) continue ;
66+ for (Field field : configClass .getDeclaredFields ()) {
67+ if (field .getAnnotation (ConfigNode .class ) == null ) continue ;
5768
58- var nameAnnotation = declaredField .getAnnotation (NodeName .class );
69+ var nameAnnotation = field .getAnnotation (NodeName .class );
70+ String nodeName = nameAnnotation != null ? nameAnnotation .value () : field .getName ();
5971
60- if (declaredField .isAnnotationPresent (Match .class )) {
61- var matchAnnotation = declaredField .getAnnotation (Match .class );
72+ var handler = ConfigTypeHandlerFactory .getHandler (field .getType ());
73+
74+ if (field .isAnnotationPresent (Match .class )) {
75+ var matchAnnotation = field .getAnnotation (Match .class );
6276 var regex = matchAnnotation .regex ();
6377 var pattern = Pattern .compile (regex );
64- var res = pattern .matcher (config .getString (nameAnnotation . value () )).matches ();
78+ var res = pattern .matcher (config .getString (nodeName )).matches ();
6579 if (!res ) {
66- logger .warn ("Configured value for " + nameAnnotation . value () + " is invalid! Falling back to default value ." );
67- configValues .add (constructDefaultField ( declaredField ));
80+ logger .warn ("Configured value for " + nodeName + " is invalid! Using default." );
81+ configValues .add (handler . getDefault ( field ));
6882 continue ;
6983 }
7084 }
7185
72- if (declaredField .getType ().equals (int .class )) {
73- var val = config .getInt (nameAnnotation .value ());
74- var constraint = declaredField .getAnnotation (IntegerConstraint .class );
75- if (constraint != null )
76- val = val > constraint .max () ? constraint .max () : Math .max (val , constraint .min ());
77- configValues .add (val );
78- } else if (declaredField .getType ().equals (boolean .class ))
79- configValues .add (config .getBoolean (nameAnnotation .value ()));
80- else if (declaredField .getType ().equals (double .class ))
81- configValues .add (config .getDouble (nameAnnotation .value ()));
82- else if (declaredField .getType ().equals (List .class ))
83- configValues .add (config .getList (nameAnnotation .value ()));
84- else configValues .add (config .getString (nameAnnotation .value ()));
86+ configValues .add (handler .getValue (config , nodeName , field ));
8587 }
8688 try {
87- // Records only have 1 constructor so just access index 0
89+ // Since records in Java have canonical constructors, we directly use the first constructor
8890 var recordConfig = configClass .getDeclaredConstructors ()[0 ].newInstance (configValues .toArray ());
8991 @ SuppressWarnings ("unchecked" ) var out = (T ) recordConfig ;
9092 return out ;
@@ -94,17 +96,37 @@ else if (declaredField.getType().equals(List.class))
9496 return null ;
9597 }
9698
99+ /**
100+ * Reloads the configuration for the given configuration class. This method simply calls getConfig.
101+ *
102+ * @param <T> The type of the configuration record.
103+ * @param configClass The class of the configuration record to reload.
104+ * @return A reloaded instance of T or null if reload fails or configClass is invalid.
105+ */
97106 public <T > T reload (Class <T > configClass ) {
98107 return getConfig (configClass );
99108 }
100109
110+ /**
111+ * Initializes the configuration file with default values defined in the record.
112+ *
113+ * @param configClass The class of the record for which configuration needs to be initialized.
114+ * @param config The Config object representing the YAML file.
115+ */
101116 private void initializeConfig (Class <?> configClass , Config config ) {
102117 var fields = configClass .getDeclaredFields ();
103118 for (Field field : fields )
104119 initializeField (field , config );
105120 }
106121
122+ /**
123+ * Initializes or retrieves the configuration file for the given configuration class.
124+ *
125+ * @param configClass The class of the configuration record.
126+ * @return A Config object representing the configuration file.
127+ */
107128 private Config initConfigFile (Class <?> configClass ) {
129+ // Determine file path and header for the config file
108130 var pathAnnotation = configClass .getAnnotation (ConfigPath .class );
109131 var filePath = pathAnnotation == null ? configClass .getSimpleName () : pathAnnotation .value ();
110132
@@ -114,53 +136,60 @@ private Config initConfigFile(Class<?> configClass) {
114136 return configManager .getNewConfig (filePath , header );
115137 }
116138
139+ /**
140+ * Initializes a single field of the configuration with its default value or annotated default.
141+ *
142+ * @param field The field from the record to initialize in the config.
143+ * @param config The Config object where the field should be initialized.
144+ */
117145 private void initializeField (Field field , Config config ) {
118146 if (field .getAnnotation (ConfigNode .class ) == null ) return ;
119147
120148 var nameAnnotation = field .getAnnotation (NodeName .class );
121- var nodeName = nameAnnotation == null ? field .getName () : nameAnnotation .value ();
149+ var nodeName = nameAnnotation != null ? nameAnnotation .value () : field .getName ();
150+
151+ var handler = ConfigTypeHandlerFactory .getHandler (field .getType ());
122152
123153 var defaultAnnotation = field .getAnnotation (NodeDefault .class );
124- var nodeDefault = defaultAnnotation == null ? constructDefaultField (field ) : parseDefault (field );
154+ var nodeDefault = defaultAnnotation != null ? parseDefault (field ) : handler . getDefault (field );
125155
126156 var commentAnnotation = field .getAnnotation (NodeComment .class );
127- var nodeComment = commentAnnotation == null ? new String []{} : commentAnnotation . value () ;
157+ var nodeComment = commentAnnotation != null ? commentAnnotation . value () : new String []{};
128158
129- if (config .get (nodeName ) != null ) return ;
130- config .set (nodeName , nodeDefault , nodeComment );
131- config .saveConfig ();
159+ if (config .get (nodeName ) == null ) {
160+ handler .setValue (config , nodeName , nodeDefault , nodeComment );
161+ config .saveConfig ();
162+ }
132163 }
133164
165+ /**
166+ * Constructs a default value for a field when no default is provided via annotations.
167+ *
168+ * @param f The field for which to construct a default value.
169+ * @return An object representing the default value for the field, or null on failure.
170+ */
134171 private Object constructDefaultField (Field f ) {
172+
135173 try {
136174 if (f .getType ().isPrimitive ()) {
137- if (f .getType ().equals (int .class ))
138- return 0 ;
139- if (f .getType ().equals (boolean .class ))
140- return false ;
141- if (f .getType ().equals (double .class ))
142- return 0.0 ;
143- if (f .getType ().equals (List .class ))
144- return new ArrayList <>();
175+ var handler = ConfigTypeHandlerFactory .getHandler (f .getType ());
176+ return handler .getDefault (f );
145177 }
146178 return f .getType ().getDeclaredConstructor ().newInstance ();
147- } catch (NoSuchMethodException | InvocationTargetException |
148- InstantiationException | IllegalAccessException e ) {
179+ } catch (Exception e ) {
149180 logger .err ("Failed to instantiate default value for field: " + f .getName ());
150181 }
151182 return null ;
152183 }
153184
185+ /**
186+ * Parses the default value from the @NodeDefault annotation for a field.
187+ *
188+ * @param field The field with a @NodeDefault annotation.
189+ * @return The parsed default value as an object, according to the field's type.
190+ */
154191 private Object parseDefault (Field field ) {
155- var defaultAnnotation = field .getAnnotation (NodeDefault .class );
156- if (field .getType ().equals (int .class ))
157- return Integer .valueOf (defaultAnnotation .value ());
158- if (field .getType ().equals (boolean .class ))
159- return Boolean .valueOf (defaultAnnotation .value ());
160- if (field .getType ().equals (double .class ))
161- return Double .valueOf (defaultAnnotation .value ());
162- if (field .getType ().equals (List .class ))
163- return Arrays .stream (defaultAnnotation .value ().split (",\\ s+" )).toList ();
164- return defaultAnnotation .value ();
192+ var handler = ConfigTypeHandlerFactory .getHandler (field .getType ());
193+ return handler .getDefault (field );
165194 }
166195}
0 commit comments