1919import java .lang .annotation .Retention ;
2020import java .lang .annotation .RetentionPolicy ;
2121import java .lang .annotation .Target ;
22+ import java .lang .reflect .AnnotatedElement ;
2223import java .lang .reflect .Field ;
2324import java .lang .reflect .Modifier ;
2425import java .util .*;
@@ -226,6 +227,8 @@ public void save() {
226227 * @param initialize If this is the initialization phase.
227228 */
228229 private void step (Class <?> parent , String path , boolean initialize ) throws ReflectiveOperationException {
230+ Map <Integer , List <Pair <String , AnnotatedElement >>> members = new TreeMap <>();
231+
229232 for (Field field : parent .getDeclaredFields ()) {
230233 // Skip field if its modifiers are invalid, or it is marked @Ignore.
231234 if (!Modifier .isStatic (field .getModifiers ()) || field .isAnnotationPresent (Ignore .class )) {
@@ -248,20 +251,8 @@ private void step(Class<?> parent, String path, boolean initialize) throws Refle
248251 continue ;
249252 }
250253
251- boolean hidden = field .isAnnotationPresent (Hidden .class );
252- boolean present = document .contains (route );
253-
254- // If this is the initial saving of the value in the document,
255- // or the field is final, so we have to override the value anyway.
256- boolean initial = (!present || Modifier .isFinal (field .getModifiers ())) && !hidden ;
257-
258- // Set the field value in the configuration document.
259- if (initial || (!hidden || present )) {
260- set (route , field , field .get (null ));
261- }
262-
263- // Set the associated comment for the route's block if present.
264- document .setComment (document .getBlock (route ), field .getAnnotation (Comment .class ));
254+ // Add the field to the member priority map.
255+ members .computeIfAbsent (getPriority (field ), key -> new ArrayList <>()).add (new Pair <>(route , field ));
265256 }
266257
267258 Class <?>[] classes = parent .getDeclaredClasses ();
@@ -291,22 +282,56 @@ private void step(Class<?> parent, String path, boolean initialize) throws Refle
291282 continue ;
292283 }
293284
294- // If the class is final, remove it so it is regenerated with default values.
295- if (Modifier .isFinal (clazz .getModifiers ())) {
296- document .remove (route );
297- }
285+ // Add the class to the member priority map.
286+ members .computeIfAbsent (getPriority (clazz ), key -> new ArrayList <>()).add (new Pair <>(route , clazz ));
287+ }
298288
299- Section section = document .getSection (route );
289+ for (List <Pair <String , AnnotatedElement >> priority : members .values ()) {
290+ for (Pair <String , AnnotatedElement > member : priority ) {
291+ String route = member .getLeft ();
300292
301- // If the section doesn't exist and is not hidden, create it.
302- if (section == null && !clazz .isAnnotationPresent (Hidden .class )) {
303- section = document .createSection (route );
304- }
293+ if (member .getRight () instanceof Field ) {
294+ Field field = (Field ) member .getRight ();
295+
296+ boolean hidden = field .isAnnotationPresent (Hidden .class );
297+ boolean present = document .contains (route );
298+
299+ // If this is the initial saving of the value in the document,
300+ // or the field is final, so we have to override the value anyway.
301+ boolean initial = (!present || Modifier .isFinal (field .getModifiers ())) && !hidden ;
302+
303+ // Set the field value in the configuration document.
304+ if (initial || (!hidden || present )) {
305+ set (route , field , field .get (null ));
306+ }
307+
308+ // Set the associated comment for the route's block if present.
309+ document .setComment (document .getBlock (route ), field .getAnnotation (Comment .class ));
310+
311+ continue ;
312+ }
313+
314+ if (member .getRight () instanceof Class ) {
315+ Class <?> clazz = (Class <?>) member .getRight ();
305316
306- // If the section exists, and we shouldn't skip, set the comment and recurse.
307- if (section != null ) {
308- document .setComment (section , clazz .getAnnotation (Comment .class ));
309- step (clazz , route + "." , false );
317+ // If the class is final, remove it so it is regenerated with default values.
318+ if (Modifier .isFinal (clazz .getModifiers ())) {
319+ document .remove (route );
320+ }
321+
322+ Section section = document .getSection (route );
323+
324+ // If the section doesn't exist and is not hidden, create it.
325+ if (section == null && !clazz .isAnnotationPresent (Hidden .class )) {
326+ section = document .createSection (route );
327+ }
328+
329+ // If the section exists, and we shouldn't skip, set the comment and recurse.
330+ if (section != null ) {
331+ document .setComment (section , clazz .getAnnotation (Comment .class ));
332+ step (clazz , route + "." , false );
333+ }
334+ }
310335 }
311336 }
312337 }
@@ -468,6 +493,17 @@ private String getRoute(Key key, String name) {
468493 return valid ? key .value () : handler .getKeyFormatter ().apply (name );
469494 }
470495
496+ /**
497+ * Gets the priority for a class member.
498+ *
499+ * @param member The member.
500+ * @return The priority.
501+ */
502+ private int getPriority (AnnotatedElement member ) {
503+ Priority annotation = member .getAnnotation (Priority .class );
504+ return annotation != null ? annotation .value () : Integer .MAX_VALUE ;
505+ }
506+
471507 /**
472508 * Changes the yaml key to the specified value.
473509 */
@@ -520,4 +556,10 @@ private String getRoute(Key key, String name) {
520556 public @interface Footer {
521557 String [] value ();
522558 }
559+
560+ @ Retention (RetentionPolicy .RUNTIME )
561+ @ Target ({ElementType .TYPE , ElementType .FIELD })
562+ public @interface Priority {
563+ int value ();
564+ }
523565}
0 commit comments