@@ -816,6 +816,133 @@ constructor with one parameter of type `SerializerContext`. If such a
816
816
constructor exists, a context object is passed to it when the serializer is
817
817
instantiated by this library.
818
818
819
+ # ## Post-processing
820
+
821
+ There are two ways to apply some post-processing to your configurations :
822
+
823
+ - The first is to annotate a method in your configuration type with the
824
+ ` @PostProcess` annotation.
825
+ - The second is to add post-processor functions to a `ConfigurationProperties`
826
+ object. These functions are then applied to some set of configuration elements
827
+ that is defined by a `ConfigurationElementFilter`.
828
+
829
+ Both ways of post-processing can be applied at the same time. In this case,
830
+ the post-processor functions added to a `ConfigurationProperties` object run
831
+ first.
832
+
833
+ # ### Post-process configurations via annotated method
834
+
835
+ One way to apply post-processing to your configuration is to annotate some
836
+ method of your configuration type with the `@PostProcess` annotation.
837
+
838
+ ` ` ` java
839
+ @Configuration
840
+ public final class Config {
841
+ private int i = 10;
842
+ private String s = "abc";
843
+
844
+ @PostProcess
845
+ private void postProcess() {
846
+ this.i = this.i * 2;
847
+ this.s = this.s.repeat(2);
848
+ }
849
+ }
850
+ ` ` `
851
+
852
+ The return type of the `@PostProcess` method must either be `void` or the same
853
+ type as the type in which that method is defined. In the first case, the method
854
+ is simply executed. In the latter case, the return value of the method replaces
855
+ the current instance when initializing a configuration. This is, in particular,
856
+ useful for Java records whose fields are final and cannot be modified.
857
+
858
+ ` ` ` java
859
+ public record Config(int i, String s) {
860
+ @PostProcess
861
+ private Config postProcess() {
862
+ return new Config(i * 2, s.repeat(2));
863
+ }
864
+ }
865
+ ` ` `
866
+
867
+ The name of the `@PostProcess` method can be any valid Java method name.
868
+ However, your configuration type is allowed to define at most one such method
869
+ and `@PostProcess` methods of parent classes are _not_ executed.
870
+
871
+ # ### Post-process configuration elements by condition
872
+
873
+ The second way to apply post-processing to your configuration is to define
874
+ a `ConfigurationElementFilter`. Such a filter implicitly defines a set of
875
+ configuration elements to which some post-processing function should be applied.
876
+ Both, filters and post-processing functions, can be added via
877
+ the `ConfigurationProperties#addPostProcessor` method at the same time and the
878
+ function is then applied to all configuration elements that are defined by the
879
+ filter.
880
+
881
+ For example, to double the values of _all_ configuration elements of type `int`,
882
+ you would add the following filter and post-processing function :
883
+
884
+ ` ` ` java
885
+ ConfigurationProperties.newBuilder()
886
+ .addPostProcessor(
887
+ // Predicate<? super ConfigurationElement<?>> filter
888
+ element -> element.type().equals(int.class),
889
+ // UnaryOperator<?> postProcessor
890
+ (Integer value) -> value * 2
891
+ )
892
+ .build();
893
+ ` ` `
894
+
895
+ Note that it is your responsibility to make sure that the filter only selects
896
+ configuration elements whose type matches the type the post-processing function
897
+ expects.
898
+
899
+ Also note, that the post-processing function will be applied regardless of
900
+ whether a configuration file contained a value for some specific element.
901
+ This means that your post-processing function should properly handle `null`
902
+ input values if, for example, you allow the input of such values.
903
+
904
+ The `ConfigurationElementFilter` interface defines static factories to
905
+ facilitate the creation of common filters :
906
+
907
+ ` ` ` java
908
+ ConfigurationElementFilter.byType(Class<?> type)
909
+ ConfigurationElementFilter.byPostProcessKey(String key)
910
+ ` ` `
911
+
912
+ The second factory creates a filter that selects all configuration elements that
913
+ are annotated with `@PostProcess` and where the `key()` method of that
914
+ annotation returns the given `key`.
915
+
916
+ In the following example, the values of `a` and `b` are doubled, the value
917
+ of `c` is tripled, `d` is set to zero, and no post-processing is applied
918
+ to `e` and `f`.
919
+
920
+ ` ` ` java
921
+ record Config(
922
+ @PostProcess(key = "double") int a,
923
+ @PostProcess(key = "double") int b,
924
+ @PostProcess(key = "tripple") int c,
925
+ @PostProcess int d,
926
+ @PostProcess(key = "missing processor") int e,
927
+ int f
928
+ ) {}
929
+
930
+ ConfigurationProperties.newBuilder()
931
+ .addPostProcessor(
932
+ ConfigurationElementFilter.byPostProcessKey("double"),
933
+ (Integer value) -> value * 2
934
+ )
935
+ .addPostProcessor(
936
+ ConfigurationElementFilter.byPostProcessKey("tripple"),
937
+ (Integer value) -> value * 3
938
+ )
939
+ .addPostProcessor(
940
+ ConfigurationElementFilter.byPostProcessKey(""),
941
+ (Integer value) -> 0
942
+ )
943
+ .build();
944
+ ` ` `
945
+
819
946
# ## Changing the type of configuration elements
820
947
821
948
Changing the type of configuration elements is not supported. If you change the
@@ -1002,7 +1129,6 @@ please [open an issue](https://github.com/Exlll/ConfigLib/issues/new) where we
1002
1129
can discuss the details.
1003
1130
1004
1131
- JSON, TOML, XML support
1005
- - Post-load/Pre-save hooks
1006
1132
- More features and control over updating/versioning
1007
1133
- More control over the ordering of fields, especially in parent/child class
1008
1134
scenarios
0 commit comments