mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 10:38:19 +08:00
Compare commits
444 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 201ef0e15d | |||
| ed05374054 | |||
| b5681f2412 | |||
| bd9923ab7e | |||
| 9dece6004e | |||
| 654782afe1 | |||
| 903f0239b2 | |||
| dda929c0a9 | |||
| 2482048449 | |||
| f54ee83938 | |||
| f5316eb320 | |||
| 500eebefde | |||
| d9cb95b2e9 | |||
| ce6554c3f1 | |||
| 7dcd690a05 | |||
| f376f22ad0 | |||
| ab586c4b00 | |||
| 64cbfaa974 | |||
| 3cab343919 | |||
| 1ad8c1f407 | |||
| 4e6db50049 | |||
| e62cef5644 | |||
| d6a56003aa | |||
| 8a5870300d | |||
| 42885b2a12 | |||
| 96216d5126 | |||
| d890ce1b94 | |||
| d1ff4f7014 | |||
| 9f3089566c | |||
| 79105bf400 | |||
| 2b0b74af92 | |||
| c65d164167 | |||
| 74bde0c7ea | |||
| 7fba61cb64 | |||
| b609e1d174 | |||
| bdbd484690 | |||
| cc5383b0f2 | |||
| 35d2653f56 | |||
| d6fa7710dc | |||
| fdb6d81bf0 | |||
| 6a03d446a3 | |||
| 4a0e6b0676 | |||
| fc4a11bc3e | |||
| e95abd6b37 | |||
| 0b4304474e | |||
| f36fb81249 | |||
| b17c157e18 | |||
| c86985017f | |||
| cb7562cd8c | |||
| ae6aaa0461 | |||
| c96a6c7e49 | |||
| 74b5e80cbc | |||
| 51c5c333e9 | |||
| f09ed5bbc5 | |||
| f0abdd87e3 | |||
| 7f091669c7 | |||
| 8dbee35757 | |||
| 1a2eaff1e6 | |||
| 36364bc09b | |||
| fcb4ced610 | |||
| 5df66e59d2 | |||
| 809cd5b268 | |||
| 661527a80f | |||
| 9883e75f05 | |||
| c3297d168f | |||
| 627e0ba391 | |||
| b29935ae47 | |||
| e1a907203e | |||
| 3cd1ddb3ad | |||
| b46e116712 | |||
| 44194d6055 | |||
| 47fd058f44 | |||
| c73fcafb15 | |||
| beb6d93f7b | |||
| d62aa14ca7 | |||
| a439ca5cde | |||
| 0736153fd6 | |||
| 0bce5f1a22 | |||
| feb43bb382 | |||
| 0bfd15aaad | |||
| 15f395a8e0 | |||
| a61040a0e2 | |||
| 8766b4d77b | |||
| 8c1214612a | |||
| 608d92f834 | |||
| ad6ab9eb36 | |||
| 0eda8d8a0f | |||
| b19691b5a4 | |||
| c7331aa556 | |||
| a440c050e5 | |||
| b9d45a9bb9 | |||
| 67482de7a9 | |||
| 04bedbef07 | |||
| a4abfb733a | |||
| 76d276436b | |||
| e7198f22d5 | |||
| 5bd20e173f | |||
| 15c4bb13e8 | |||
| b74eb5c035 | |||
| ed3a68af1e | |||
| 447b82c880 | |||
| 5700f8c1c6 | |||
| 1cf230d6b6 | |||
| 1ffd4b2f0b | |||
| 92ce780d6f | |||
| a2de3303e8 | |||
| 47a1981002 | |||
| b97bc5d1a9 | |||
| cc927fdc0e | |||
| 5dc2693106 | |||
| 472ce66ca7 | |||
| 796771554f | |||
| 28cb5d4c83 | |||
| a670aec986 | |||
| 51c54b9b53 | |||
| fa99385ff0 | |||
| 70a3b893a6 | |||
| c32a941906 | |||
| 8b597d10f9 | |||
| cedfcfd262 | |||
| 12add75232 | |||
| 4d29a24768 | |||
| 8626b86961 | |||
| 754dc1de1f | |||
| 3d1d4ca609 | |||
| 990b10b6f0 | |||
| 7462cd720e | |||
| 6bc83eb3b3 | |||
| dbec551c84 | |||
| 82cca5eca2 | |||
| 65f3cc1b3d | |||
| fae048dd69 | |||
| 854e3df49f | |||
| f6167c3b5e | |||
| ffe3e88b3b | |||
| caa3077f48 | |||
| 05dbf0b504 | |||
| 810e95198e | |||
| 10004f16b4 | |||
| 8e19748c7c | |||
| 17762a2e70 | |||
| 7dbd607a3f | |||
| 035e8a227e | |||
| 04eaf6606d | |||
| e558e93410 | |||
| e55fe3a8d5 | |||
| 8f075b99b5 | |||
| 09a0b3c373 | |||
| 4df4977733 | |||
| 6434feb980 | |||
| bc3e4b3e6f | |||
| c2a9e2254c | |||
| 251dd208af | |||
| afd1bbfc0f | |||
| a699f6c164 | |||
| 844cbfab53 | |||
| f74d5d29f9 | |||
| 6f28abebb9 | |||
| 842cd78ce3 | |||
| 11f1f36a15 | |||
| ee9f29ba30 | |||
| f5bccdaad5 | |||
| f5f70ff69b | |||
| 3e221740bc | |||
| 96d09be977 | |||
| 5d7c946db5 | |||
| 9e008ff4cd | |||
| df19da170b | |||
| 3473ef2247 | |||
| d543530305 | |||
| d81855697c | |||
| 5c16e98f30 | |||
| 3f1ffadeff | |||
| d8191b7c6d | |||
| ba66220f92 | |||
| 730d6d7e9c | |||
| 4523190cb0 | |||
| 2a49e2ee6b | |||
| 77b223b2cb | |||
| 8e7ac263e7 | |||
| 80f03ec501 | |||
| 1c002ae535 | |||
| a4659c5c9f | |||
| 1c27c2f881 | |||
| 476cb79e2f | |||
| bf716b06ae | |||
| 00170e6d77 | |||
| 1bac201427 | |||
| cd77639df5 | |||
| 5ae756929a | |||
| a9e80aecce | |||
| 477f0d11bc | |||
| 53ff38a76f | |||
| c32bea3864 | |||
| 15823bb076 | |||
| f303f56199 | |||
| 22f2674e7c | |||
| 9eb9c9e13d | |||
| 616314c7f0 | |||
| 05f504a347 | |||
| bf6ea97b99 | |||
| 9847399e56 | |||
| 18515d4a78 | |||
| 9f1fc5bf90 | |||
| e7847b2166 | |||
| 69cec281be | |||
| 28dc9b9e55 | |||
| c60ba074d9 | |||
| e88bf301cc | |||
| f2c9750c35 | |||
| c0f0e989c6 | |||
| 6490edf65e | |||
| 14a5f41649 | |||
| 0b16ae08db | |||
| 020d0df3a3 | |||
| f3bdff1560 | |||
| d3ec93cef2 | |||
| 96ed604cd9 | |||
| eff91b0496 | |||
| 8db7ecf539 | |||
| 1b49e466a9 | |||
| 6df9f2ce42 | |||
| aedc6cb439 | |||
| c79b94b719 | |||
| 3a3533ce81 | |||
| 4cdd184fe6 | |||
| 18199cd1cf | |||
| 47e2a4854c | |||
| 5b95824bb0 | |||
| 2cda4ae11c | |||
| e840f6bd50 | |||
| 90407dcd0d | |||
| 79f59bafe6 | |||
| 05ff61a9d9 | |||
| c68d2371ee | |||
| 2093091923 | |||
| 45ca8b02d4 | |||
| 7f09e6cdf7 | |||
| 205db6e5b9 | |||
| 8ac1faf300 | |||
| 374a6198d8 | |||
| 6ee23ded3b | |||
| 0c7cc5c156 | |||
| 4988bb15ae | |||
| 8b24e8dbff | |||
| ce9d858f6c | |||
| efc837b6f0 | |||
| 44e864ef0f | |||
| ce33d8ff9d | |||
| de70c11f21 | |||
| a182b9af99 | |||
| c56499c0e9 | |||
| c9150ecb50 | |||
| 5075a4c96d | |||
| 30a9f3634f | |||
| 022acb97a5 | |||
| 97eda55ec7 | |||
| 7fe77b1363 | |||
| 9f9f02df51 | |||
| 41a53b1c89 | |||
| 9626ed8f49 | |||
| 119f93f234 | |||
| f2b6069389 | |||
| 6e2808380a | |||
| 3ca44aa233 | |||
| 4de9ebd118 | |||
| 217cd66bfd | |||
| f1f9a55e01 | |||
| 530d7b3391 | |||
| 2d381c3ce4 | |||
| 08626ae8c2 | |||
| 7fe2e74582 | |||
| a49180bb1d | |||
| 69a4b5e918 | |||
| 27c3c7803c | |||
| c95b1ae4d4 | |||
| c7f928686d | |||
| 4ab84cfc71 | |||
| d347d3de23 | |||
| 2b48e3146b | |||
| d35958a943 | |||
| b7990d4de3 | |||
| f01a15a19d | |||
| 85625bfe05 | |||
| cb59471f9b | |||
| 112803342a | |||
| fc1792113d | |||
| da3d4d1fd2 | |||
| b912ea369c | |||
| 2df33e3458 | |||
| d041828717 | |||
| c7fb51d6b2 | |||
| 2ef1471f59 | |||
| 20a6eaefcd | |||
| e419e2498f | |||
| 6ef4f9aebd | |||
| 21a8906ad9 | |||
| c91794c611 | |||
| 3769cf4438 | |||
| 34bc7601ee | |||
| 0d19dc236e | |||
| 9647591b0d | |||
| 97db8204dc | |||
| 48050a52a2 | |||
| e42de0eee7 | |||
| 814dae2278 | |||
| db19531c13 | |||
| d0af3e436f | |||
| 1252baab76 | |||
| 83037baf10 | |||
| adf90a983b | |||
| 02d816cd8d | |||
| 5d5626bff2 | |||
| 363c1e7ed8 | |||
| 7884d49309 | |||
| b77e64e6e6 | |||
| 505b86ec9c | |||
| 374f646f9e | |||
| aa50345329 | |||
| db8b227317 | |||
| 60eed8a14d | |||
| d41630be72 | |||
| 1c3a2b01ea | |||
| 1ede74c692 | |||
| 67456a8aac | |||
| f422900fb5 | |||
| b3e0007eba | |||
| 89c8ec8adf | |||
| e8f49bf8bc | |||
| 8ac980fdbb | |||
| 9f2e4bc0cb | |||
| ae16d131c3 | |||
| bd8470a6e8 | |||
| 6af2d3fef5 | |||
| 00f83002c5 | |||
| 0aa548cbbf | |||
| 9c0ed1c5c2 | |||
| bb0998cbac | |||
| 1eb16f00b6 | |||
| 5ccf63b423 | |||
| f1c0c74574 | |||
| 8ee074474c | |||
| 27a68ead7c | |||
| c6cce5208f | |||
| bc67de06f6 | |||
| b668794f5d | |||
| 07424284b7 | |||
| 81e024e309 | |||
| 763fc7c758 | |||
| 56557221a4 | |||
| e4435bf883 | |||
| eee4a278d9 | |||
| 3a0a8e79b9 | |||
| 3b2b1b27cc | |||
| d84ea1b7da | |||
| a1f2cdca04 | |||
| c52183aadd | |||
| d71aabad2d | |||
| 6a007c5187 | |||
| 43b00f2b69 | |||
| 2e61e66cdb | |||
| 39f946c28e | |||
| 25931ffd7e | |||
| de103da879 | |||
| 457c22d461 | |||
| aa4225dbba | |||
| ddd33154be | |||
| 727c26a2fb | |||
| 9c95a16d90 | |||
| 92c05f1a59 | |||
| 739ed41885 | |||
| a66da01996 | |||
| 6dc0447502 | |||
| c49d904665 | |||
| b756074ddc | |||
| 9e3dff3e95 | |||
| fd01d9b7ef | |||
| 0f8383bbf3 | |||
| 1232c7c4da | |||
| 7ac39da4e9 | |||
| bf89f583db | |||
| 03c69ba3a2 | |||
| d9cbd1a283 | |||
| 96e90dd71b | |||
| a9f3d829bd | |||
| 8eefba5159 | |||
| 01e20df559 | |||
| 35398ab741 | |||
| 6f97166192 | |||
| ccd239ad6b | |||
| 69b27476bc | |||
| 0651cac6b0 | |||
| 1a1efad283 | |||
| 3c1ba61b61 | |||
| 1a3e84a787 | |||
| 00228db2c4 | |||
| 0fddfe28af | |||
| 4a17089da0 | |||
| 8faa7b1c24 | |||
| 5e525428fe | |||
| bc0dfd5698 | |||
| dc28d743db | |||
| f61294c5f3 | |||
| 6883a464db | |||
| bcdf0d9bd1 | |||
| 5f89ff4db7 | |||
| d6f4970277 | |||
| c045ca1489 | |||
| ceea900b08 | |||
| 85bacb24f3 | |||
| 6b3a353fcc | |||
| 2c026fc0b0 | |||
| f8b4bbd3a9 | |||
| e9a0f0ff30 | |||
| e3fe6e7c80 | |||
| c179fa2ccd | |||
| 390815b790 | |||
| 760ac815df | |||
| 216050a701 | |||
| 6d0ee35197 | |||
| 494491cf94 | |||
| 00e88b50ff | |||
| 033236c89b | |||
| 791fa6e5b4 | |||
| 51c287a0a7 | |||
| ab2f898164 | |||
| 4f4b203240 | |||
| c94fef893f | |||
| c2a08c6c72 | |||
| dd7a6c819f | |||
| e9c010981e | |||
| 78d52e1aae | |||
| 42ccc23347 | |||
| 85578e3d8c | |||
| dc4d3664d3 | |||
| 2e4cb5480a | |||
| 4b7a7aeae7 | |||
| 132ca81635 | |||
| 80747ac922 | |||
| 0d10a06547 | |||
| 0bda97d82a | |||
| a13ea7569c | |||
| c9f488c932 | |||
| 72584f66ac |
@@ -0,0 +1,6 @@
|
||||
# configured Javadoc
|
||||
|
||||
Based
|
||||
on [Github Pages](https://pages.github.com/),
|
||||
please
|
||||
see [JavaDoc](https://carmjos.github.io/configured) 。
|
||||
@@ -0,0 +1,3 @@
|
||||
# Documentation
|
||||
|
||||
See [wiki](https://github.com/CarmJos/configured/wiki).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
@@ -1,9 +0,0 @@
|
||||
# EasyConfiguration Javadoc
|
||||
|
||||
基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/EasyConfiguration) 。
|
||||
|
||||
## 如何实现?
|
||||
|
||||
若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions)
|
||||
自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/) ,
|
||||
可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
[*.java]
|
||||
indent_size = 4
|
||||
max_line_length = 120
|
||||
ij_java_align_consecutive_assignments = false
|
||||
ij_java_align_consecutive_variable_declarations = false
|
||||
ij_java_align_group_field_declarations = false
|
||||
ij_java_align_multiline_annotation_parameters = false
|
||||
ij_java_align_multiline_array_initializer_expression = false
|
||||
ij_java_align_multiline_assignment = false
|
||||
ij_java_align_multiline_binary_operation = false
|
||||
ij_java_align_multiline_chained_methods = false
|
||||
ij_java_align_multiline_deconstruction_list_components = true
|
||||
ij_java_align_multiline_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_align_types_in_multi_catch = true
|
||||
ij_java_annotation_new_line_in_record_component = false
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = off
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = off
|
||||
ij_java_assignment_wrap = off
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = off
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_field_with_annotations = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 0
|
||||
ij_java_blank_lines_between_record_components = 0
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_add_space = false
|
||||
ij_java_block_comment_at_first_column = true
|
||||
ij_java_builder_methods =
|
||||
ij_java_call_parameters_new_line_after_left_paren = false
|
||||
ij_java_call_parameters_right_paren_on_new_line = false
|
||||
ij_java_call_parameters_wrap = off
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 5
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_deconstruction_list_wrap = normal
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = false
|
||||
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
|
||||
ij_java_do_while_brace_force = never
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = false
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_entity_dd_prefix =
|
||||
ij_java_entity_dd_suffix = EJB
|
||||
ij_java_entity_eb_prefix =
|
||||
ij_java_entity_eb_suffix = Bean
|
||||
ij_java_entity_hi_prefix =
|
||||
ij_java_entity_hi_suffix = Home
|
||||
ij_java_entity_lhi_prefix = Local
|
||||
ij_java_entity_lhi_suffix = Home
|
||||
ij_java_entity_li_prefix = Local
|
||||
ij_java_entity_li_suffix =
|
||||
ij_java_entity_pk_class = java.lang.String
|
||||
ij_java_entity_ri_prefix =
|
||||
ij_java_entity_ri_suffix =
|
||||
ij_java_entity_vo_prefix =
|
||||
ij_java_entity_vo_suffix = VO
|
||||
ij_java_enum_constants_wrap = off
|
||||
ij_java_enum_field_annotation_wrap = off
|
||||
ij_java_extends_keyword_wrap = off
|
||||
ij_java_extends_list_wrap = off
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_field_name_prefix =
|
||||
ij_java_field_name_suffix =
|
||||
ij_java_filter_class_prefix =
|
||||
ij_java_filter_class_suffix =
|
||||
ij_java_filter_dd_prefix =
|
||||
ij_java_filter_dd_suffix =
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = never
|
||||
ij_java_for_statement_new_line_after_left_paren = false
|
||||
ij_java_for_statement_right_paren_on_new_line = false
|
||||
ij_java_for_statement_wrap = off
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
ij_java_generate_use_type_annotation_before_type = true
|
||||
ij_java_if_brace_force = never
|
||||
ij_java_imports_layout = @*, *, |, javax.**, java.**, |, $*
|
||||
ij_java_indent_case_from_switch = true
|
||||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_insert_override_annotation = true
|
||||
ij_java_keep_blank_lines_before_right_brace = 2
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
||||
ij_java_keep_blank_lines_in_code = 2
|
||||
ij_java_keep_blank_lines_in_declarations = 2
|
||||
ij_java_keep_builder_methods_indents = false
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = false
|
||||
ij_java_keep_simple_lambdas_in_one_line = false
|
||||
ij_java_keep_simple_methods_in_one_line = false
|
||||
ij_java_label_indent_absolute = false
|
||||
ij_java_label_indent_size = 0
|
||||
ij_java_lambda_brace_style = end_of_line
|
||||
ij_java_layout_on_demand_import_from_same_package_first = true
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_line_comment_add_space = false
|
||||
ij_java_line_comment_add_space_on_reformat = false
|
||||
ij_java_line_comment_at_first_column = true
|
||||
ij_java_listener_class_prefix =
|
||||
ij_java_listener_class_suffix =
|
||||
ij_java_local_variable_name_prefix =
|
||||
ij_java_local_variable_name_suffix =
|
||||
ij_java_message_dd_prefix =
|
||||
ij_java_message_dd_suffix = EJB
|
||||
ij_java_message_eb_prefix =
|
||||
ij_java_message_eb_suffix = Bean
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = off
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = off
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_multi_catch_types_wrap = normal
|
||||
ij_java_names_count_to_use_import_on_demand = 3
|
||||
ij_java_new_line_after_lparen_in_annotation = false
|
||||
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
|
||||
ij_java_new_line_after_lparen_in_record_header = false
|
||||
ij_java_new_line_when_body_is_presented = false
|
||||
ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
|
||||
ij_java_parameter_annotation_wrap = off
|
||||
ij_java_parameter_name_prefix =
|
||||
ij_java_parameter_name_suffix =
|
||||
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_java_place_assignment_sign_on_next_line = false
|
||||
ij_java_prefer_longer_names = true
|
||||
ij_java_prefer_parameters_wrap = false
|
||||
ij_java_preserve_module_imports = true
|
||||
ij_java_record_components_wrap = normal
|
||||
ij_java_repeat_annotations =
|
||||
ij_java_repeat_synchronized = true
|
||||
ij_java_replace_instanceof_and_cast = false
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = off
|
||||
ij_java_rparen_on_new_line_in_annotation = false
|
||||
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_servlet_class_prefix =
|
||||
ij_java_servlet_class_suffix =
|
||||
ij_java_servlet_dd_prefix =
|
||||
ij_java_servlet_dd_suffix =
|
||||
ij_java_session_dd_prefix =
|
||||
ij_java_session_dd_suffix = EJB
|
||||
ij_java_session_eb_prefix =
|
||||
ij_java_session_eb_suffix = Bean
|
||||
ij_java_session_hi_prefix =
|
||||
ij_java_session_hi_suffix = Home
|
||||
ij_java_session_lhi_prefix = Local
|
||||
ij_java_session_lhi_suffix = Home
|
||||
ij_java_session_li_prefix = Local
|
||||
ij_java_session_li_suffix =
|
||||
ij_java_session_ri_prefix =
|
||||
ij_java_session_ri_suffix =
|
||||
ij_java_session_si_prefix =
|
||||
ij_java_session_si_suffix = Service
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = false
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_deconstruction_list = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = false
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_annotation_eq = true
|
||||
ij_java_spaces_around_assignment_operators = true
|
||||
ij_java_spaces_around_bitwise_operators = true
|
||||
ij_java_spaces_around_equality_operators = true
|
||||
ij_java_spaces_around_lambda_arrow = true
|
||||
ij_java_spaces_around_logical_operators = true
|
||||
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||
ij_java_spaces_around_multiplicative_operators = true
|
||||
ij_java_spaces_around_relational_operators = true
|
||||
ij_java_spaces_around_shift_operators = true
|
||||
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||
ij_java_spaces_around_unary_operator = false
|
||||
ij_java_spaces_inside_block_braces_when_body_is_present = false
|
||||
ij_java_spaces_within_angle_brackets = false
|
||||
ij_java_spaces_within_annotation_parentheses = false
|
||||
ij_java_spaces_within_array_initializer_braces = false
|
||||
ij_java_spaces_within_braces = false
|
||||
ij_java_spaces_within_brackets = false
|
||||
ij_java_spaces_within_cast_parentheses = false
|
||||
ij_java_spaces_within_catch_parentheses = false
|
||||
ij_java_spaces_within_deconstruction_list = false
|
||||
ij_java_spaces_within_for_parentheses = false
|
||||
ij_java_spaces_within_if_parentheses = false
|
||||
ij_java_spaces_within_method_call_parentheses = false
|
||||
ij_java_spaces_within_method_parentheses = false
|
||||
ij_java_spaces_within_parentheses = false
|
||||
ij_java_spaces_within_record_header = false
|
||||
ij_java_spaces_within_switch_parentheses = false
|
||||
ij_java_spaces_within_synchronized_parentheses = false
|
||||
ij_java_spaces_within_try_parentheses = false
|
||||
ij_java_spaces_within_while_parentheses = false
|
||||
ij_java_special_else_if_treatment = true
|
||||
ij_java_static_field_name_prefix =
|
||||
ij_java_static_field_name_suffix =
|
||||
ij_java_subclass_name_prefix =
|
||||
ij_java_subclass_name_suffix = Impl
|
||||
ij_java_switch_expressions_wrap = normal
|
||||
ij_java_ternary_operation_signs_on_next_line = false
|
||||
ij_java_ternary_operation_wrap = off
|
||||
ij_java_test_name_prefix =
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = off
|
||||
ij_java_throws_list_wrap = off
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = off
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = never
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
ij_java_wrap_semicolon_after_call_chain = false
|
||||
|
||||
[*.xml]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
max_line_length = 120
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
max_line_length = 120
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
max_line_length = 120
|
||||
|
||||
[*.md]
|
||||
max_line_length = 500
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,2 +1 @@
|
||||
github: [ CarmJos ]
|
||||
custom: [ 'https://donate.carm.cc' ]
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
---
|
||||
name: 问题提交
|
||||
about: 描述问题并提交,帮助我们对其进行检查与修复。
|
||||
title: ''
|
||||
labels: bug
|
||||
name: Submit bugs&issues
|
||||
about: Describe the problem and submit it to help us review and fix it.
|
||||
title: 'fix: '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### **问题简述**
|
||||
### **Description**
|
||||
|
||||
用简短的话语描述一下大概问题。
|
||||
<!-- Describe the general problem in short words.-->
|
||||
|
||||
### **问题来源**
|
||||
### **Operations**
|
||||
|
||||
描述一下通过哪些操作才发现的问题,如:
|
||||
<!--
|
||||
Describe the problem discovered through what operations, such as:
|
||||
|
||||
1. 使用了 '...'
|
||||
2. 输入了 '....'
|
||||
3. 出现了报错 '....'
|
||||
1. Clicked '...'
|
||||
2. Typed '....'
|
||||
3. Error says '....'
|
||||
-->
|
||||
|
||||
### **预期结果** (可选)
|
||||
### **Expected result** _(Optional)_
|
||||
|
||||
如果问题不发生,应该是什么情况
|
||||
### **Screenshots & Error Logs**
|
||||
|
||||
### **问题截图/问题报错**
|
||||
### **Environment**
|
||||
|
||||
如果有报错或输出,请提供截图。
|
||||
- System: `Windows 10` / `Ubuntu` / `...`
|
||||
- Java version: `JDK11` / `OPENJDK8` / `JRE8` / `...`
|
||||
|
||||
### **操作环境**
|
||||
### **Anything else...**
|
||||
|
||||
- 系统环境: `Windows 10` / `Ubuntu` / `...`
|
||||
- Java版本: `JDK11` / `OPENJDK8` / `JRE8` / `...`
|
||||
|
||||
### **其他补充**
|
||||
|
||||
如有其他补充,可以在这里描述。
|
||||
<!-- If there are other supplements, they can be described here. -->
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
---
|
||||
name: 功能需求
|
||||
about: 希望我们提供更多的功能。
|
||||
title: ''
|
||||
name: Features
|
||||
about: Ask for new features.
|
||||
title: 'feat: '
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### **功能简述**
|
||||
### **Description**
|
||||
|
||||
简单的描述一下你想要的功能
|
||||
<!-- Describe the features in short words.-->
|
||||
|
||||
### **需求来源**
|
||||
### **Source**
|
||||
|
||||
简单的描述一下为什么需要这个功能。
|
||||
<!-- Describe the reason that you need this feature in short words.-->
|
||||
|
||||
### **功能参考**(可选)
|
||||
### **Examples** _(Optional)_
|
||||
|
||||
如果有相关功能的参考,如文本、截图,请提供给我们。
|
||||
<!--Any screenshots or example codes please.-->
|
||||
|
||||
### **附加内容**
|
||||
### **Additional details**
|
||||
|
||||
如果有什么小细节需要重点注意,请在这里告诉我们。
|
||||
<!--If there are any small details that need to be highlighted, please let us know here.-->
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
# This workflow checks out code, performs a Codacy security scan
|
||||
# and integrates the results with the
|
||||
# GitHub Advanced Security code scanning feature. For more information on
|
||||
# the Codacy security scan action usage and parameters, see
|
||||
# https://github.com/codacy/codacy-analysis-cli-action.
|
||||
# For more information on Codacy Analysis CLI in general, see
|
||||
# https://github.com/codacy/codacy-analysis-cli.
|
||||
|
||||
name: "Codacy Security Scan"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '27 16 * * 5'
|
||||
|
||||
jobs:
|
||||
codacy-security-scan:
|
||||
name: Codacy Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
verbose: true
|
||||
output: results.sarif
|
||||
format: sarif
|
||||
# Adjust severity of non-security issues
|
||||
gh-code-scanning-compat: true
|
||||
# Force 0 exit code to allow SARIF file generation
|
||||
# This will handover control about PR rejection to the GitHub side
|
||||
max-allowed-issues: 2147483647
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
@@ -38,11 +38,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: "Project Deploy"
|
||||
name: "Deploy & Publish"
|
||||
|
||||
on:
|
||||
# 支持手动触发构建
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '21'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
server-id: github
|
||||
@@ -40,14 +40,14 @@ jobs:
|
||||
run: |
|
||||
rm -rf docs
|
||||
mkdir -vp docs
|
||||
cp -vrf core/target/apidocs/* docs/
|
||||
cp -vrf .documentation/JAVADOC-README.md docs/README.md
|
||||
cp -vrf core/target/reports/apidocs/* docs/
|
||||
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
||||
|
||||
- name: "Generate the Javadoc sitemap"
|
||||
id: sitemap
|
||||
uses: cicirello/generate-sitemap@v1
|
||||
with:
|
||||
base-url-path: https://CarmJos.github.io/EasyConfiguration
|
||||
base-url-path: https://CarmJos.github.io/configured
|
||||
path-to-root: docs
|
||||
|
||||
- name: "Output stats"
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
run: |
|
||||
cd docs
|
||||
git init
|
||||
git remote add origin git@github.com:CarmJos/EasyConfiguration.git
|
||||
git remote add origin git@github.com:CarmJos/configured.git
|
||||
git checkout -b gh-pages
|
||||
git add -A
|
||||
git commit -m "API Document generated."
|
||||
@@ -87,22 +87,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '21'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
server-id: ossrh
|
||||
server-id: central
|
||||
server-username: MAVEN_USERNAME
|
||||
server-password: MAVEN_PASSWORD
|
||||
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
|
||||
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
|
||||
|
||||
- name: "Central Deploy"
|
||||
run: mvn -B -Possrh deploy --file pom.xml -DskipTests
|
||||
run: mvn -B -Pcentral deploy --file pom.xml -DskipTests
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
|
||||
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
|
||||
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Project Build & Tests
|
||||
name: Build & Tests
|
||||
|
||||
on:
|
||||
# 支持手动触发构建
|
||||
@@ -15,18 +15,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '21'
|
||||
distribution: 'adopt'
|
||||
- name: "Package"
|
||||
run: mvn -B package --file pom.xml -Dgpg.skip
|
||||
- name: "Target Stage"
|
||||
run: mkdir staging && cp */target/*.jar staging
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Artifact
|
||||
path: staging
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
/.idea/
|
||||
.idea/*
|
||||
!.idea/icon.svg
|
||||
**/target/
|
||||
**.iml
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule ".wiki"]
|
||||
path = .wiki
|
||||
url = https://github.com/CarmJos/configured.wiki.git
|
||||
Generated
+36
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
Submodule
+1
Submodule .wiki added at c79c6f4a43
@@ -0,0 +1,75 @@
|
||||
# Code of Conduct
|
||||
|
||||
(English primary version – a brief Chinese note may follow. 中文提示:本文件英文为主,若理解存在困难可联系维护者。)
|
||||
|
||||
This project adopts the principles of the Contributor Covenant v2.1 (https://www.contributor-covenant.org/version/2/1/code_of_conduct/) with project‑specific clarifications below. By participating you agree to uphold this Code.
|
||||
|
||||
## Our Pledge
|
||||
We strive to provide a harassment‑free, inclusive, friendly, and productive environment for everyone, regardless of age, body, disability, ethnicity, sex characteristics, gender identity or expression, level of experience, education, socio‑economic status, nationality, personal appearance, race, caste, religion (or lack thereof), sexual identity or orientation, or technical choices.
|
||||
|
||||
## Our Standards
|
||||
Examples of behavior that contributes to a positive environment:
|
||||
- Showing empathy and respect to all participants
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Focusing on what is best for the project and community
|
||||
- Being honest about mistakes and seeking improvement
|
||||
- Using welcoming and inclusive language
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
- Harassment, discrimination, or derogatory comments
|
||||
- Trolling, insulting or antagonistic remarks, personal or political attacks
|
||||
- Public or private harassment or sustained disruption of activities
|
||||
- Publishing others’ private information without explicit permission
|
||||
- Sexualized language, imagery, or unwelcome advances
|
||||
- Any other conduct reasonably deemed inappropriate or unsafe
|
||||
|
||||
## Scope
|
||||
This Code applies within all project spaces (repository code, issues, pull requests, discussions, wiki, CI logs) and in public spaces whenever an individual is representing the project or community.
|
||||
|
||||
## Responsibilities & Enforcement
|
||||
Project maintainers ("maintainers") are responsible for clarifying standards and taking appropriate, fair, and consistent corrective action.
|
||||
|
||||
Maintainers may remove or edit contributions that violate this Code (comments, commits, code, wiki edits, issues, discussions) and may temporarily or permanently ban any contributor for abusive, harassing, or otherwise harmful behavior.
|
||||
|
||||
## Reporting
|
||||
Report violations privately via:
|
||||
- Email: carm@carm.cc
|
||||
|
||||
Please include (if possible):
|
||||
- Links, timestamps, or message IDs
|
||||
- Description of what happened and why it is a concern
|
||||
- Screenshots or logs (if relevant)
|
||||
- Preferred or suggested resolution
|
||||
|
||||
We aim to acknowledge reports within 72 hours and provide an initial assessment within 7 days. All good‑faith reports will be treated confidentially and only shared with individuals who need the information to resolve the issue.
|
||||
|
||||
## Enforcement Guidelines
|
||||
Maintainers will use these guidelines to determine the impact of an incident and consequences:
|
||||
|
||||
1. Correction
|
||||
- Impact: Use of inappropriate language or other unprofessional conduct.
|
||||
- Consequence: Private or public warning, request for change.
|
||||
2. Warning
|
||||
- Impact: A single severe incident or repeated inappropriate behavior.
|
||||
- Consequence: Official warning; continued misconduct leads to escalation.
|
||||
3. Temporary Suspension
|
||||
- Impact: Persistent violations despite previous warnings.
|
||||
- Consequence: Temporary participation suspension (issues/PRs/discussions). Conditions for reinstatement communicated.
|
||||
4. Permanent Ban
|
||||
- Impact: Demonstrated pattern of harassment, hate, or threats; refusal to reform.
|
||||
- Consequence: Permanent removal from community spaces.
|
||||
|
||||
## Conflicts of Interest
|
||||
Maintainers will recuse themselves from enforcement decisions where they have a personal conflict. A neutral maintainer or external trusted community member may be asked to assist when appropriate.
|
||||
|
||||
## Appeals
|
||||
If you believe an enforcement action was made in error or was unfair, you may appeal by emailing the same reporting address within 14 days, providing context and justification.
|
||||
|
||||
## Attribution
|
||||
Portions adapted from Contributor Covenant v2.1 and other open source community best practices.
|
||||
|
||||
## Changes & Versioning
|
||||
Substantive changes to this Code will be announced in the repository (Release Notes / CHANGELOG / Discussions). Historical versions will remain accessible via Git history.
|
||||
|
||||
---
|
||||
If unsure whether something is acceptable: choose respect, transparency, and ask a maintainer before acting.
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
# Contributing Guide
|
||||
|
||||
> English is the primary language. A brief Chinese hint: 若需中文协助可在 Issue 中说明。
|
||||
|
||||
Thank you for investing time in contributing! This document describes how to propose changes and how we maintain quality and consistency across the project.
|
||||
|
||||
## Quick Links
|
||||
- Code of Conduct: ./CODE_OF_CONDUCT.md
|
||||
- Security Policy: ./SECURITY.md
|
||||
- Issues: https://github.com/CarmJos/configured/issues
|
||||
- Discussions / Q&A: (open an Issue if Discussions are disabled)
|
||||
|
||||
## Table of Contents
|
||||
1. Principles
|
||||
2. Scope of Contributions
|
||||
3. Getting Started (Environment & Build)
|
||||
4. Project Structure
|
||||
5. Branching & Workflow
|
||||
6. Issue Workflow
|
||||
7. Pull Request Guidelines
|
||||
8. Commit Message Convention
|
||||
9. Coding Standards
|
||||
10. Testing Guidelines
|
||||
11. Documentation & JavaDoc
|
||||
12. Dependency Policy
|
||||
13. Versioning & Releases
|
||||
14. Performance Expectations
|
||||
15. Internationalization / Language
|
||||
16. FAQ for Contributors
|
||||
17. Attribution
|
||||
|
||||
---
|
||||
## 1. Principles
|
||||
We value: correctness, clarity, minimalism, maintainability, security-by-default, and performance without premature complexity. Every contribution should move at least one of these forward while not regressing the others.
|
||||
|
||||
## 2. Scope of Contributions
|
||||
Acceptable contributions include (but are not limited to):
|
||||
- Bug fixes & test coverage improvements
|
||||
- Performance optimizations with measurable benefit
|
||||
- New configuration providers (storage backends) with generic value
|
||||
- Validation or serialization helpers
|
||||
- Documentation, examples, or tutorials
|
||||
- Tooling that improves developer productivity or release robustness
|
||||
|
||||
Out-of-scope (likely to be declined):
|
||||
- Vendor lock‑in features narrowly targeting one proprietary platform (unless optional & isolated)
|
||||
- Large feature branches without prior design discussion
|
||||
- Unbounded abstraction that increases complexity with unclear user value
|
||||
|
||||
## 3. Getting Started (Environment & Build)
|
||||
Requirements:
|
||||
- JDK 8 (minimum). Later JDKs may work but target bytecode is 1.8.
|
||||
- Maven 3.8+ (Wrapper optional; project assumes standard mvn).
|
||||
|
||||
Build all modules:
|
||||
```bash
|
||||
mvn -q clean verify
|
||||
```
|
||||
Skip tests (NOT recommended for PR validation):
|
||||
```bash
|
||||
mvn -q clean install -DskipTests
|
||||
```
|
||||
Run a single module:
|
||||
```bash
|
||||
mvn -q -pl core -am test
|
||||
```
|
||||
Generate JavaDoc (already bound in build):
|
||||
```bash
|
||||
mvn -q javadoc:javadoc
|
||||
```
|
||||
|
||||
## 4. Project Structure (High-level)
|
||||
- core/ : Fundamental abstractions (Configuration, Value types, factories)
|
||||
- features/ : Optional, orthogonal enhancements (validators, section, text, etc.)
|
||||
- providers/ : Concrete persistence / parsing backends (yaml, gson, hocon, sql, mongodb, temp)
|
||||
- demo/ : Usage demonstrations & sample scenarios
|
||||
|
||||
Rules:
|
||||
- Core must not depend on feature or provider modules.
|
||||
- Features must not form cycles; prefer depending only on core.
|
||||
- Providers should keep external dependencies minimal and shaded/isolated only if necessary.
|
||||
|
||||
## 5. Branching & Workflow
|
||||
- main (or master): Stable; only fast‑forward / squash from reviewed PRs.
|
||||
- feature/<short-name>: New feature work. Open draft PR early for feedback.
|
||||
- fix/<issue-id>-<slug>: Bug fix referencing an Issue.
|
||||
- chore/<topic>: Build, infra, docs improvements.
|
||||
|
||||
Never force push to main. Force pushes allowed only to your own feature branches.
|
||||
|
||||
## 6. Issue Workflow
|
||||
1. Search existing issues first to avoid duplication.
|
||||
2. Provide reproduction steps (minimal code or config) for bugs.
|
||||
3. Label suggestions as enhancement; performance items as perf.
|
||||
4. For larger features, open a design issue summarizing: Problem, Motivation, Proposed API, Alternatives.
|
||||
|
||||
## 7. Pull Request Guidelines
|
||||
Checklist before opening a PR:
|
||||
- Linked to at least one Issue (unless trivial doc fix)
|
||||
- Passes `mvn verify`
|
||||
- Adds or updates tests covering new behavior / bug
|
||||
- Includes JavaDoc / README / CHANGELOG fragment if user-facing
|
||||
- No unrelated refactors or formatting churn
|
||||
- Minimal diff: avoid reordering imports unless enforced by style
|
||||
|
||||
Review expectations:
|
||||
- Maintainers strive to respond within 5 business days.
|
||||
- Use constructive, action‑oriented comments.
|
||||
- Resolve conversations or explain why not.
|
||||
- Squash commits if they are noisy; retain meaningful logical grouping.
|
||||
|
||||
## 8. Commit Message Convention
|
||||
Use Conventional Commits (https://www.conventionalcommits.org/) with optional scope:
|
||||
```
|
||||
<type>(<scope>): <short imperative summary>
|
||||
|
||||
<body>(optional)
|
||||
|
||||
<footer>(breaking changes, issue references)
|
||||
```
|
||||
Types used:
|
||||
- feat: New feature (user visible)
|
||||
- fix: Bug fix
|
||||
- perf: Performance improvement
|
||||
- refactor: Internal restructuring without behavior change
|
||||
- docs: Documentation only
|
||||
- test: Add or fix tests
|
||||
- build: Build system or dependency changes
|
||||
- ci: Continuous integration changes
|
||||
- chore: Maintenance tasks
|
||||
- style: Formatting (rare; avoid large style‑only changes)
|
||||
|
||||
Breaking changes: add `!` after type/scope or include `BREAKING CHANGE:` footer.
|
||||
|
||||
## 9. Coding Standards
|
||||
- Java: Follow effective Java principles; prefer explicit types over inference for public APIs.
|
||||
- Nullability: Use JetBrains annotations (`@NotNull`, `@Nullable`) where helpful.
|
||||
- Immutability: Favor immutable value objects; avoid exposing mutable internal state.
|
||||
- Exceptions: Use specific exception types; no swallowing silently. Validate inputs early.
|
||||
- Logging: Keep core logging minimal; let consumers decide verbosity. Avoid println.
|
||||
- APIs: Minimize surface; avoid exposing prematurely general interfaces.
|
||||
- Annotations: Provide meaningful config path / comments metadata clearly.
|
||||
|
||||
### Style
|
||||
- Indentation: 4 spaces.
|
||||
- Line length guideline: ≤ 140 chars (soft limit).
|
||||
- Avoid wildcard imports.
|
||||
|
||||
## 10. Testing Guidelines
|
||||
- Use JUnit (current: JUnit 4). Prefer deterministic, isolated tests.
|
||||
- Each bug fix must include a regression test failing before the fix.
|
||||
- Avoid time‑sensitive sleeps; prefer deterministic constructs.
|
||||
- Keep provider-specific integration tests under provider module.
|
||||
- Use random data cautiously; if used, log seed for reproduction.
|
||||
|
||||
Command:
|
||||
```bash
|
||||
mvn -q test
|
||||
```
|
||||
|
||||
## 11. Documentation & JavaDoc
|
||||
- Public classes & methods: brief JavaDoc describing contract, thread-safety, nullability.
|
||||
- Add code examples when clarifying complex usage.
|
||||
- Update README or module README for new feature flags or environment variables.
|
||||
- Keep demo module aligned with latest recommended usage.
|
||||
|
||||
## 12. Dependency Policy
|
||||
- Keep transitive dependency footprint lean.
|
||||
- No large frameworks for simple utilities.
|
||||
- Justify each new dependency in PR description (purpose, size, maintenance risk).
|
||||
- Prefer stable, well-adopted libraries with permissive licenses compatible with LGPL.
|
||||
- Security-sensitive libs (parsers, DB drivers) should be periodically updated.
|
||||
|
||||
## 13. Versioning & Releases
|
||||
- Follows Semantic Versioning (MAJOR.MINOR.PATCH).
|
||||
- Public API additions => MINOR bump.
|
||||
- Backwards-compatible bug fix => PATCH.
|
||||
- Backwards-incompatible change => MAJOR (document rationale & migration).
|
||||
- Release steps (maintainers):
|
||||
1. Ensure main is green (CI all passing)
|
||||
2. Update CHANGELOG (if present) or Release Notes draft
|
||||
3. Bump versions via maven-release-plugin (ensure GPG & staging configured)
|
||||
4. Tag + push; verify publication (Central / GitHub Packages)
|
||||
5. Publish GitHub Release with highlights + migration notes
|
||||
|
||||
## 14. Performance Expectations
|
||||
- Avoid unnecessary object churn in hot paths.
|
||||
- Profile before large rewrites. Provide benchmark or allocation stats if claiming improvement.
|
||||
- Defer I/O and heavy parsing until needed (lazy loading pattern).
|
||||
|
||||
## 15. Internationalization / Language
|
||||
- Primary language: English for code, comments, issues, PRs.
|
||||
- Chinese clarifications acceptable if accompanied by English.
|
||||
|
||||
## 16. FAQ for Contributors
|
||||
Q: Can I add a new provider?
|
||||
A: Yes—open a design Issue first summarizing data model, external dependencies, and test strategy.
|
||||
|
||||
Q: How do I mark experimental APIs?
|
||||
A: Add JavaDoc: `@apiNote Experimental: subject to change without notice.` and avoid wide promotion.
|
||||
|
||||
Q: Why Java 8 target?
|
||||
A: Maximizes compatibility across server & embedded environments.
|
||||
|
||||
## 17. Attribution
|
||||
Portions inspired by widely adopted open-source guidelines (Kotlin, Spring, Apache projects) and Conventional Commits.
|
||||
|
||||
---
|
||||
Thank you for helping build a robust configuration ecosystem!
|
||||
|
||||
@@ -1,21 +1,166 @@
|
||||
MIT License
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (c) 2022 Carm
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -1,40 +1,139 @@
|
||||
```text
|
||||
____ _____ ____ __ _
|
||||
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
|
||||
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
|
||||
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
|
||||
/___/ /___/
|
||||
<div align=center>
|
||||
<img src=".doc/images/banner.png" alt="Banner"/>
|
||||
|
||||
[](https://github.com/CarmJos/configured/releases)
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||
[](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
|
||||
[](https://www.codefactor.io/repository/github/carmjos/configured)
|
||||

|
||||
|
||||
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
|
||||
</div>
|
||||
|
||||
# configured _(config framework)_
|
||||
|
||||
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
|
||||
|
||||
_**"Once set, Simple get."**_
|
||||
|
||||
A simple, easy-to-use and universal solution for managing, loading, reading,
|
||||
and updating configuration files.
|
||||
|
||||
Supported **JSON**, **YAML**, **Hocon**, **TOML**, **SQL**, **MongoDB**... and much more!
|
||||
|
||||
## Features & Advantages
|
||||
|
||||
Supported [YAML](providers/yaml), [JSON](providers/json), [HOCON](providers/hocon) and [SQL](providers/sql)
|
||||
based configuration files format.
|
||||
|
||||
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience
|
||||
and efficiency.
|
||||
- Supports manual serialization and deserialization of complex configurations.
|
||||
- Offers multiple builder forms for rapid construction of `ConfigValue<?>` objects.
|
||||
- Enables specification of configuration paths, comments, and more via annotations.
|
||||
|
||||
## Development
|
||||
|
||||
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/configured).
|
||||
|
||||
For a detailed development guide, see [wiki](https://github.com/CarmJos/configured/wiki).
|
||||
|
||||
### Preview
|
||||
|
||||
To quickly demonstrate the applicability of the project, here are a few practical demonstrations:
|
||||
|
||||
- [Database configuration.](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
|
||||
- [Demonstration of configurations instance classes.](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
|
||||
|
||||
Check out all code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java).
|
||||
For more examples, see the [Development Guide](.doc/README.md).
|
||||
|
||||
```java
|
||||
|
||||
@ConfigPath(root = true)
|
||||
@HeaderComments("Configurations for sample")
|
||||
public interface SampleConfig extends Configuration {
|
||||
|
||||
@InlineComment("Enabled?") // Inline comment
|
||||
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
||||
|
||||
@HeaderComments("Server configurations") // Header comment
|
||||
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
||||
|
||||
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
|
||||
@FooterComments("[ UUID >-----------------------------------")
|
||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||
.parse(UUID::fromString).serialize(UUID::toString)
|
||||
.defaults(
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||
).build();
|
||||
|
||||
@ConfigPath("info") // Custom path
|
||||
interface INFO extends Configuration {
|
||||
|
||||
@HeaderComments("Configure your name!") // Header comment
|
||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||
|
||||
@ConfigPath("how-old-are-you") // Custom path
|
||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
# EasyConfiguration
|
||||
```java
|
||||
public class Sample {
|
||||
public static void main(String[] args) {
|
||||
// 1. Make a configuration provider from a file.
|
||||
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||
.resourcePath("configs/sample.yml")
|
||||
.indent(4) // Optional: Set the indentation of the configuration file.
|
||||
.build();
|
||||
|
||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
|
||||
[](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
|
||||

|
||||

|
||||
// 2. Initialize the configuration classes or instances.
|
||||
holder.initialize(SampleConfig.class);
|
||||
// 3. Enjoy using the configuration!
|
||||
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve()); // true
|
||||
SampleConfig.ENABLED.set(false);
|
||||
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve()); // false
|
||||
// p.s. Changes not save so enable value will still be true in the next run.
|
||||
|
||||
轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。
|
||||
System.out.println("Your name is " + SampleConfig.INFO.NAME.resolve() + " (age=" + SampleConfig.INFO.AGE.resolve() + ")!");
|
||||
}
|
||||
}
|
||||
|
||||
## 优势
|
||||
```
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
```yaml
|
||||
# Configurations for sample
|
||||
|
||||
## 开发
|
||||
enabled: true #Enabled?
|
||||
|
||||
详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release) 请 [点击这里](https://carmjos.github.io/EasyConfiguration) 。
|
||||
# Server configurations
|
||||
port:
|
||||
|
||||
### 示例代码
|
||||
# [ UUID >-----------------------------------
|
||||
# A lot of UUIDs
|
||||
uuids:
|
||||
- 00000000-0000-0000-0000-000000000000
|
||||
- 00000000-0000-0000-0000-000000000001
|
||||
# [ UUID >-----------------------------------
|
||||
|
||||
您可以 [点击这里](impl/yaml/src/test/java/config/source/TestConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
|
||||
info:
|
||||
# Configure your name!
|
||||
name: Joker
|
||||
how-old-are-you: 24
|
||||
```
|
||||
|
||||
### 依赖方式
|
||||
### Dependencies
|
||||
|
||||
#### Maven 依赖
|
||||
#### Maven Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```xml
|
||||
|
||||
@@ -42,24 +141,17 @@
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>configured</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/configured</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
@@ -69,24 +161,32 @@
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<artifactId>configured-core</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<!-- YAML file-based implementation, compatible with all Java environments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
<artifactId>configured-yaml</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON file-based implementation, compatible with all Java environments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-gson</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
@@ -97,125 +197,73 @@
|
||||
|
||||
</details>
|
||||
|
||||
**基于Spigot实现的版本**
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-spigot</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
**基于Bungee实现的版本 (不支持自动注释)**
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-bungee</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
#### Gradle Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
||||
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
|
||||
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
// YAML file-based implementation, compatible with all Java environments.
|
||||
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
|
||||
|
||||
// JSON file-based implementation, compatible with all Java environments.
|
||||
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>平台依赖版本</summary>
|
||||
## Derived Projects
|
||||
|
||||
```groovy
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
|
||||
|
||||
dependencies {
|
||||
configured for MineCraft!
|
||||
Easily manage configurations on MineCraft-related server platforms.
|
||||
|
||||
// 用于Spigot服务端的版本
|
||||
api "cc.carm.lib:easyconfiguration-spigot:[LATEST RELEASE]"
|
||||
Currently, it supports BungeeCord, Velocity, Bukkit (Spigot) servers,
|
||||
with more platforms to be supported soon.
|
||||
|
||||
// 用于BungeeCord服务端的版本,不支持注解。
|
||||
// 如需注解,可选择使用 `easyconfiguration-yaml` 并打包。
|
||||
api "cc.carm.lib:easyconfiguration-bungee:[LATEST RELEASE]"
|
||||
## Support and Donation
|
||||
|
||||
}
|
||||
```
|
||||
If you appreciate this plugin, consider supporting me with a [donation](https://github.com/sponsors/CarmJos)!
|
||||
|
||||
</details>
|
||||
Thank you for supporting open-source projects!
|
||||
|
||||
## 支持与捐赠
|
||||
Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
|
||||
|
||||
若您觉得本插件做的不错,您可以通过捐赠支持我!
|
||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
Many thanks to [ArtformGames](https://github.com/ArtformGames) for their
|
||||
strong support and active contribution to this project!
|
||||
|
||||
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/>
|
||||
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||
|
||||
## 开源协议
|
||||
## Open Source License
|
||||
|
||||
本项目源码采用 [The MIT License](https://opensource.org/licenses/MIT) 开源协议。
|
||||
This project's source code is licensed under
|
||||
the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||
|
||||
<details>
|
||||
<summary>关于 MIT 协议</summary>
|
||||
|
||||
> MIT 协议可能是几大开源协议中最宽松的一个,核心条款是:
|
||||
>
|
||||
> 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版 权和许可提示。
|
||||
>
|
||||
> 这意味着:
|
||||
>
|
||||
> - 你可以自由使用,复制,修改,可以用于自己的项目。
|
||||
> - 可以免费分发或用来盈利。
|
||||
> - 唯一的限制是必须包含许可声明。
|
||||
>
|
||||
> MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。
|
||||
>
|
||||
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
|
||||
|
||||
</details>
|
||||
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
<div align=center>
|
||||
<img src=".doc/images/banner.png" alt="Banner"/>
|
||||
|
||||
[](https://github.com/CarmJos/configured/releases)
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||
[](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
|
||||
[](https://www.codefactor.io/repository/github/carmjos/configured)
|
||||

|
||||
|
||||
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
||||
|
||||
</div>
|
||||
|
||||
# configured _(配置文件框架)_
|
||||
|
||||
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
|
||||
|
||||
**一次配置,轻松读取!**
|
||||
|
||||
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
|
||||
|
||||
## 特性 & 优势
|
||||
|
||||
支持 [YAML](providers/yaml), [JSON](providers/gson), [HOCON](providers/hocon) 和 [SQL](providers/sql) 等多种配置文件格式。
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
- 支持复杂配置的手动序列化、反序列化。
|
||||
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
||||
- 支持通过注解规定配置对应的路径、注释等信息。
|
||||
|
||||
## 开发
|
||||
|
||||
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release)
|
||||
请 [点击这里](https://CarmJos.github.io/configured) 。
|
||||
|
||||
### 示例代码
|
||||
|
||||
为快速的展示该项目的适用性,这里有几个实际演示:
|
||||
|
||||
- [数据库配置文件实例](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
|
||||
- [全种类配置实例类演示](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
|
||||
|
||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo)
|
||||
直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](https://github.com/CarmJos/configured/wiki) 。
|
||||
|
||||
```java
|
||||
|
||||
@ConfigPath(root = true)
|
||||
@HeaderComments("Configurations for sample")
|
||||
public interface SampleConfig extends Configuration {
|
||||
|
||||
@InlineComment("Enabled?") // 行后注释
|
||||
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
||||
|
||||
@HeaderComments("Server configurations") // 头部注释
|
||||
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
||||
|
||||
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
|
||||
@FooterComments("[ UUID >-----------------------------------")
|
||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||
.parse(UUID::fromString).serialize(UUID::toString)
|
||||
.defaults(
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||
).build();
|
||||
|
||||
interface INFO extends Configuration {
|
||||
|
||||
@HeaderComments("Configure your name!") // Header comment
|
||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||
|
||||
@ConfigPath("how-old-are-you") // 自定义路径
|
||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
public class Sample {
|
||||
public static void main(String[] args) {
|
||||
// 1. 生成一个 “holder” 用于给配置类提供源配置的文件。
|
||||
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||
.resourcePath("configs/sample.yml")
|
||||
.indent(4) // Optional: Set the indentation of the configuration file.
|
||||
.build();
|
||||
|
||||
// 2. 通过 “holder” 初始化配置类或配置实例。
|
||||
holder.initialize(SampleConfig.class);
|
||||
// 3. 现在可以享受快捷方便的配置文件使用方式了~
|
||||
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
|
||||
SampleConfig.ENABLED.set(false);
|
||||
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve());
|
||||
// p.s. 在本示例里的更改未保存,因此启用值在下次运行中仍将为 true。
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Configurations for sample
|
||||
|
||||
enabled: true #Enabled?
|
||||
|
||||
# Server configurations
|
||||
port:
|
||||
|
||||
# [ UUID >-----------------------------------
|
||||
# A lot of UUIDs
|
||||
uuids:
|
||||
- 00000000-0000-0000-0000-000000000000
|
||||
- 00000000-0000-0000-0000-000000000001
|
||||
# [ UUID >-----------------------------------
|
||||
|
||||
info:
|
||||
# Configure your name!
|
||||
name: Joker
|
||||
how-old-are-you: 24
|
||||
```
|
||||
|
||||
### 依赖方式
|
||||
|
||||
#### Maven 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<id>configured</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/configured</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-core</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-yaml</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于JSON文件的实现版本,可用于全部Java环境。-->
|
||||
<!--需要注意的是,JSON不支持文件注释。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-gson</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-hocon</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>configured-sql</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
|
||||
|
||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||
//需要注意的是,JSON不支持文件注释。
|
||||
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
|
||||
|
||||
api "cc.carm.lib:configured-hocon:[LATEST RELEASE]"
|
||||
|
||||
api "cc.carm.lib:configured-sql:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 衍生项目
|
||||
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
||||
|
||||
configured for MineCraft!
|
||||
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
||||
|
||||
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
||||
|
||||
## 支持与捐赠
|
||||
|
||||
若您觉得本插件做的不错,您可以通过[捐赠](https://github.com/sponsors/CarmJos)支持我!
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
|
||||
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
|
||||
|
||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
|
||||
|
||||
万分感谢来自 [ArtformGames](https://github.com/ArtformGames) 对本项目的大力支持与积极贡献!
|
||||
|
||||
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
# Security Policy
|
||||
|
||||
English is the authoritative language of this document.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We generally provide security fixes only for the latest released MINOR version (most recent tag on the default branch). Older versions may receive fixes only if the vulnerability is critical and a patch is low risk.
|
||||
|
||||
| Version | Status |
|
||||
|----------|-------------------------|
|
||||
| Latest | Security fixes |
|
||||
| < Latest | Not routinely supported |
|
||||
|
||||
If you rely on an older version you are strongly encouraged to upgrade promptly after each release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please DO NOT open a public Issue for suspected security problems.
|
||||
|
||||
Instead, email: carm@carm.cc with:
|
||||
|
||||
- A clear description of the issue and potential impact
|
||||
- Steps to reproduce (minimal code / configuration)
|
||||
- Affected version(s) and environment (JDK, OS)
|
||||
- Any known workarounds
|
||||
- Preferred public credit name (optional)
|
||||
|
||||
You will receive an acknowledgement within 72 hours (workdays) confirming receipt.
|
||||
|
||||
## Assessment & Disclosure Process
|
||||
|
||||
1. Triage & validation (attempt reproduction, scope impact)
|
||||
2. Determine severity (CVSS or qualitative: Low / Moderate / High / Critical)
|
||||
3. Prepare a private fix / patch + regression tests
|
||||
4. Coordinate an embargoed release window (typically ≤14 days after validation for High/Critical)
|
||||
5. Release a new version (and possibly backport if warranted)
|
||||
6. Publish security advisory (GitHub Security Advisory + Release Notes) including mitigation steps
|
||||
|
||||
We may reject reports that are clearly non‑security bugs (e.g., feature requests, performance tuning) or issues requiring unreasonable preconditions (e.g., attacker already has full local code execution).
|
||||
|
||||
## Non-Qualifying Issues (Examples)
|
||||
|
||||
- Missing rate limits on non-authenticated, non-state-changing operations
|
||||
- Denial-of-service requiring unrealistic resource constraints or already solved via JVM flags
|
||||
- Vulnerabilities only exploitable on unsupported / EOL Java versions
|
||||
- Social engineering, SPF/DMARC issues beyond this codebase’s control
|
||||
|
||||
## Coordinated Disclosure
|
||||
|
||||
If you plan to blog or speak publicly about the vulnerability prior to patch availability, please coordinate timing so users can upgrade safely.
|
||||
|
||||
## Dependency Security
|
||||
|
||||
We periodically review dependency versions for CVEs. You can help by:
|
||||
|
||||
- Submitting PRs that upgrade vulnerable libraries with changelog & compatibility notes
|
||||
- Avoiding unnecessary new dependencies
|
||||
|
||||
## Cryptographic Material
|
||||
|
||||
This project does not bundle custom cryptographic primitives. If you discover misuse of crypto APIs or insecure random number use in security-sensitive areas, treat it as a security report.
|
||||
|
||||
## Reporting Format Template (Recommended)
|
||||
|
||||
```
|
||||
Subject: [Security Report] <short title>
|
||||
|
||||
Affected Component: (module / class)
|
||||
Version(s) Tested: x.y.z (and earlier if known)
|
||||
Environment: JDK x, OS
|
||||
|
||||
Summary:
|
||||
Describe the vulnerability and impact.
|
||||
|
||||
Reproduction Steps:
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
Expected vs Actual:
|
||||
|
||||
Potential Impact:
|
||||
|
||||
Workarounds / Mitigations (if any):
|
||||
|
||||
Credit: (name / handle / anonymous)
|
||||
```
|
||||
|
||||
## Credit & Acknowledgement
|
||||
|
||||
We will list (with permission) reporters who submit valid, first responsibly disclosed security issues in the release notes / advisory.
|
||||
|
||||
## GPG / Integrity
|
||||
|
||||
Release artifacts are signed (see project docs). Always verify signatures and checksums when consuming artifacts from Maven Central or GitHub Packages.
|
||||
|
||||
## Questions
|
||||
|
||||
For general (non-sensitive) questions, open an Issue labeled `question` rather than using the security email.
|
||||
|
||||
Thank you for helping keep the ecosystem safe.
|
||||
|
||||
+15
-6
@@ -3,19 +3,28 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<artifactId>configured-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>1.0.4</version>
|
||||
<version>4.2.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<artifactId>configured-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Configured - Core</name>
|
||||
<url>https://github.com/CarmJos/configured</url>
|
||||
<description>
|
||||
The core module of Configured framework,
|
||||
providing essential configuration management functionality.
|
||||
</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -37,4 +46,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
/**
|
||||
* The root interface of the configuration file interfaces,
|
||||
* which is used to label a class as a configuration.
|
||||
*/
|
||||
public interface Configuration {
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Value adapter, used to convert the value of the configuration file into the objects.
|
||||
*
|
||||
* @param <TYPE> The type of the target value
|
||||
*/
|
||||
public class ValueAdapter<TYPE>
|
||||
implements ValueSerializer<TYPE>, ValueParser<TYPE> {
|
||||
|
||||
protected final @NotNull ValueType<TYPE> type;
|
||||
|
||||
protected @Nullable ValueSerializer<TYPE> serializer;
|
||||
protected @Nullable ValueParser<TYPE> deserializer;
|
||||
|
||||
public ValueAdapter(@NotNull ValueType<TYPE> type) {
|
||||
this(type, null, null);
|
||||
}
|
||||
|
||||
public ValueAdapter(@NotNull ValueType<TYPE> type,
|
||||
@Nullable ValueSerializer<TYPE> serializer,
|
||||
@Nullable ValueParser<TYPE> parser) {
|
||||
this.type = type;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = parser;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<TYPE> type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public @Nullable ValueSerializer<TYPE> serializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
public @Nullable ValueParser<TYPE> parser() {
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
public ValueAdapter<TYPE> serializer(@Nullable ValueSerializer<TYPE> serializer) {
|
||||
this.serializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueAdapter<TYPE> parser(@Nullable ValueParser<TYPE> deserializer) {
|
||||
this.deserializer = deserializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object serialize(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type,
|
||||
@NotNull TYPE value
|
||||
) throws Exception {
|
||||
if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
|
||||
return serializer.serialize(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TYPE parse(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type,
|
||||
@NotNull Object value
|
||||
) throws Exception {
|
||||
if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
|
||||
return deserializer.parse(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ValueAdapter)) return false;
|
||||
ValueAdapter<?> that = (ValueAdapter<?>) o;
|
||||
return Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
|
||||
public class ValueAdapterRegistry {
|
||||
|
||||
protected final Set<ValueAdapter<?>> adapters = new HashSet<>();
|
||||
protected final Map<ValueType<?>, ValueAdapter<?>> adapterCache = new HashMap<>();
|
||||
|
||||
public <FROM, TO> void register(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||
@Nullable DataFunction<FROM, TO> parser,
|
||||
@Nullable DataFunction<TO, FROM> serializer) {
|
||||
register(ValueType.of(from), ValueType.of(to), parser, serializer);
|
||||
}
|
||||
|
||||
public <FROM, TO> void register(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||
@Nullable DataFunction<FROM, TO> parser,
|
||||
@Nullable DataFunction<TO, FROM> serializer) {
|
||||
ValueAdapter<FROM> fromAdapter = adapterOf(from);
|
||||
if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
|
||||
register(to,
|
||||
serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
|
||||
parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.parse(provider, from, data))
|
||||
);
|
||||
}
|
||||
|
||||
public void register(@NotNull ValueAdapter<?>... adapter) {
|
||||
adapters.addAll(Arrays.asList(adapter));
|
||||
adapterCache.clear();
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
register(ValueType.of(type), serializer);
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
existing.serializer(serializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, serializer, null));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull Class<T> type, @NotNull ValueParser<T> deserializer) {
|
||||
register(ValueType.of(type), deserializer);
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueParser<T> deserializer) {
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
existing.parser(deserializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, null, deserializer));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type,
|
||||
@Nullable ValueSerializer<T> serializer,
|
||||
@Nullable ValueParser<T> deserializer) {
|
||||
if (serializer == null && deserializer == null) return;
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
if (serializer != null) existing.serializer(serializer);
|
||||
if (deserializer != null) existing.parser(deserializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, serializer, deserializer));
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(@NotNull Class<?> type) {
|
||||
unregister(ValueType.of(type));
|
||||
}
|
||||
|
||||
public void unregister(@NotNull ValueType<?> type) {
|
||||
adapters.removeIf(adapter -> adapter.type().equals(type));
|
||||
adapterCache.clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) {
|
||||
if (adapterCache.containsKey(type)) {
|
||||
return (ValueAdapter<T>) adapterCache.get(type);
|
||||
}
|
||||
|
||||
for (ValueAdapter<?> adapter : adapters) {
|
||||
if (adapter.type().equals(type)) {
|
||||
adapterCache.put(type, adapter);
|
||||
return (ValueAdapter<T>) adapter;
|
||||
}
|
||||
}
|
||||
|
||||
for (ValueAdapter<?> adapter : adapters) {
|
||||
if (adapter.type().isSubtypeOf(type)) {
|
||||
adapterCache.put(type, adapter);
|
||||
return (ValueAdapter<T>) adapter;
|
||||
}
|
||||
}
|
||||
|
||||
adapterCache.put(type, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> ValueAdapter<T> adapterOf(@NotNull T value) {
|
||||
return adapterOf(ValueType.of(value));
|
||||
}
|
||||
|
||||
public <T> ValueAdapter<T> adapterOf(@NotNull Class<T> type) {
|
||||
return adapterOf(ValueType.of(type));
|
||||
}
|
||||
|
||||
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
return deserialize(holder, ValueType.of(type), source);
|
||||
}
|
||||
|
||||
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||
if (source == null) return null;
|
||||
|
||||
Type typeInstance = type.getType();
|
||||
if (!(typeInstance instanceof ParameterizedType) && type.isInstance(source)) {
|
||||
return type.cast(source);
|
||||
}
|
||||
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter != null) {
|
||||
return adapter.parse(holder, type, source);
|
||||
}
|
||||
|
||||
return deserializeWithoutAdapter(holder, type, source, typeInstance);
|
||||
}
|
||||
|
||||
private <T> T deserializeWithoutAdapter(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
|
||||
@NotNull Object source, @NotNull Type typeInstance) throws Exception {
|
||||
Class<?> rawType = type.getRawType();
|
||||
|
||||
if (rawType.isArray()) {
|
||||
return deserializeArray(holder, type, source, rawType);
|
||||
}
|
||||
|
||||
if (typeInstance instanceof ParameterizedType) {
|
||||
return deserializeParameterized(holder, type, source, (ParameterizedType) typeInstance);
|
||||
}
|
||||
|
||||
throw new RuntimeException("No adapter for type " + type);
|
||||
}
|
||||
|
||||
private <T> T deserializeArray(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
|
||||
@NotNull Object source, @NotNull Class<?> rawType) throws Exception {
|
||||
List<?> list;
|
||||
if (source instanceof List<?>) {
|
||||
list = (List<?>) source;
|
||||
} else {
|
||||
// For non-list sources, treat as single element array
|
||||
list = Collections.singletonList(source);
|
||||
}
|
||||
|
||||
int size = list.size();
|
||||
if (size == 0) {
|
||||
return type.cast(Array.newInstance(rawType.getComponentType(), 0));
|
||||
}
|
||||
|
||||
Class<?> componentType = rawType.getComponentType();
|
||||
Object[] array = (Object[]) Array.newInstance(componentType, size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
array[i] = deserialize(holder, componentType, list.get(i));
|
||||
}
|
||||
return type.cast(array);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T deserializeParameterized(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
|
||||
@NotNull Object source, @NotNull ParameterizedType pt) throws Exception {
|
||||
Type rawType = pt.getRawType();
|
||||
Type[] typeArgs = pt.getActualTypeArguments();
|
||||
|
||||
if (rawType == List.class || rawType == Collection.class || rawType == ArrayList.class) {
|
||||
return (T) deserializeCollection(holder, source, typeArgs[0], ArrayList::new);
|
||||
}
|
||||
|
||||
if (rawType == Set.class || rawType == HashSet.class) {
|
||||
return (T) deserializeCollection(holder, source, typeArgs[0], HashSet::new);
|
||||
}
|
||||
|
||||
if (rawType == Map.class || rawType == LinkedHashMap.class) {
|
||||
return (T) deserializeMap(holder, source, typeArgs[0], typeArgs[1]);
|
||||
}
|
||||
|
||||
throw new RuntimeException("No adapter for parameterized type " + type);
|
||||
}
|
||||
|
||||
private Collection<?> deserializeCollection(@NotNull ConfigurationHolder<?> holder, @NotNull Object source,
|
||||
@NotNull Type elementType, @NotNull java.util.function.Supplier<Collection<Object>> collectionFactory) throws Exception {
|
||||
ValueType<?> elementValueType = ValueType.of(elementType);
|
||||
List<?> sourceList = deserializeList(holder, elementValueType, source);
|
||||
|
||||
if (sourceList.isEmpty()) {
|
||||
return collectionFactory.get();
|
||||
}
|
||||
|
||||
Collection<Object> result = collectionFactory.get();
|
||||
if (result instanceof ArrayList) {
|
||||
((ArrayList<Object>) result).ensureCapacity(sourceList.size());
|
||||
}
|
||||
|
||||
for (Object item : sourceList) {
|
||||
Object deserializedItem = deserialize(holder, elementValueType, item);
|
||||
if (deserializedItem != null) {
|
||||
result.add(deserializedItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<Object, Object> deserializeMap(@NotNull ConfigurationHolder<?> holder, @NotNull Object source,
|
||||
@NotNull Type keyType, @NotNull Type valueType) throws Exception {
|
||||
Map<?, ?> sourceMap;
|
||||
if (source instanceof Map<?, ?>) {
|
||||
sourceMap = (Map<?, ?>) source;
|
||||
} else if (source instanceof ConfigureSection) {
|
||||
sourceMap = ((ConfigureSection) source).asMap();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot deserialize to Map from " + source.getClass());
|
||||
}
|
||||
|
||||
int mapSize = sourceMap.size();
|
||||
if (mapSize == 0) {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
ValueType<?> keyValueType = ValueType.of(keyType);
|
||||
ValueType<?> valueValueType = ValueType.of(valueType);
|
||||
Map<Object, Object> resultMap = new LinkedHashMap<>(mapSize);
|
||||
|
||||
for (Map.Entry<?, ?> entry : sourceMap.entrySet()) {
|
||||
Object key = deserialize(holder, keyValueType, entry.getKey());
|
||||
Object value = deserialize(holder, valueValueType, entry.getValue());
|
||||
resultMap.put(key, value);
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception {
|
||||
if (value == null) return null; // Null check
|
||||
|
||||
ValueType<T> type = ValueType.of(value);
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter != null) return adapter.serialize(holder, type, value);
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] array = (Object[]) value;
|
||||
List<Object> serializedList = new ArrayList<>(array.length);
|
||||
for (Object item : array) {
|
||||
serializedList.add(serialize(holder, item));
|
||||
}
|
||||
return serializedList;
|
||||
} else if (value instanceof Collection<?>) {
|
||||
Collection<?> collection = (Collection<?>) value;
|
||||
List<Object> serializedList = new ArrayList<>(collection.size());
|
||||
for (Object item : collection) {
|
||||
serializedList.add(serialize(holder, item));
|
||||
}
|
||||
return serializedList;
|
||||
} else if (value instanceof Map<?, ?>) {
|
||||
Map<?, ?> map = (Map<?, ?>) value;
|
||||
Map<Object, Object> serializedMap = new LinkedHashMap<>(map.size());
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
Object key = serialize(holder, entry.getKey());
|
||||
Object val = serialize(holder, entry.getValue());
|
||||
serializedMap.put(key, val);
|
||||
}
|
||||
return serializedMap;
|
||||
}
|
||||
|
||||
return value; // No adapters, and cannot handle, try to return the original value
|
||||
}
|
||||
|
||||
protected <T> List<T> deserializeList(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||
if (source == null) return Collections.emptyList(); // Null check
|
||||
if (source instanceof List<?>) {
|
||||
List<?> list = (List<?>) source;
|
||||
List<T> result = new ArrayList<>(list.size());
|
||||
for (Object item : list) {
|
||||
T deserializedItem = deserialize(holder, type, item);
|
||||
if (deserializedItem != null) {
|
||||
result.add(deserializedItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else { // Maybe singleton? Let's try to deserialize it as a single element list
|
||||
T deserializedItem = deserialize(holder, type, source);
|
||||
if (deserializedItem != null) {
|
||||
return Collections.singletonList(deserializedItem);
|
||||
} else return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Value deserializer, convert base data to target value.
|
||||
*
|
||||
* @param <TYPE> The type of target value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ValueParser<TYPE> {
|
||||
|
||||
@Nullable TYPE parse(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type, @NotNull Object data
|
||||
) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Value serializer, convert target value to base data.
|
||||
*
|
||||
* @param <TYPE> The type of value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ValueSerializer<TYPE> {
|
||||
|
||||
@Nullable Object serialize(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
|
||||
) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* {@link ValueType} used to get the generic type of the value,
|
||||
* It can be used to check if an object is an instance of a specific type,
|
||||
* and to cast objects to the correct type.
|
||||
* <p>
|
||||
* Java's type system is not capable of retaining generic type information at runtime.
|
||||
* This class is used to represent a type with its generic parameters.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class ValueType<T> {
|
||||
|
||||
public static final ValueType<Object> OBJECT = ofPrimitiveType(Object.class);
|
||||
public static final ValueType<String> STRING = ofPrimitiveType(String.class);
|
||||
public static final ValueType<Integer> INTEGER = ofPrimitiveType(Integer.class);
|
||||
public static final ValueType<Integer> INTEGER_TYPE = ofPrimitiveType(int.class);
|
||||
public static final ValueType<Long> LONG = ofPrimitiveType(Long.class);
|
||||
public static final ValueType<Long> LONG_TYPE = ofPrimitiveType(long.class);
|
||||
public static final ValueType<Double> DOUBLE = ofPrimitiveType(Double.class);
|
||||
public static final ValueType<Double> DOUBLE_TYPE = ofPrimitiveType(double.class);
|
||||
public static final ValueType<Float> FLOAT = ofPrimitiveType(Float.class);
|
||||
public static final ValueType<Float> FLOAT_TYPE = ofPrimitiveType(float.class);
|
||||
public static final ValueType<Boolean> BOOLEAN = ofPrimitiveType(Boolean.class);
|
||||
public static final ValueType<Boolean> BOOLEAN_TYPE = ofPrimitiveType(boolean.class);
|
||||
public static final ValueType<Byte> BYTE = ofPrimitiveType(Byte.class);
|
||||
public static final ValueType<Byte> BYTE_TYPE = ofPrimitiveType(byte.class);
|
||||
public static final ValueType<Short> SHORT = ofPrimitiveType(Short.class);
|
||||
public static final ValueType<Short> SHORT_TYPE = ofPrimitiveType(short.class);
|
||||
public static final ValueType<Character> CHAR = ofPrimitiveType(Character.class);
|
||||
public static final ValueType<Character> CHAR_TYPE = ofPrimitiveType(char.class);
|
||||
|
||||
public static final ValueType<?>[] PRIMITIVE_TYPES = {
|
||||
STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR,
|
||||
INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ValueType<T> of(@NotNull T value) {
|
||||
return of((Class<T>) value.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ValueType<T> of(final Type type) {
|
||||
if (type == null) throw new NullPointerException("Type cannot be null");
|
||||
if (type instanceof Class<?>) { // Try to fast handle primitive types
|
||||
Class<?> clazz = (Class<?>) type;
|
||||
for (ValueType<?> valueType : PRIMITIVE_TYPES) {
|
||||
if (valueType.getRawType() == clazz) {
|
||||
return (ValueType<T>) valueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ValueType<T>(type) {
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> ValueType<T> of(final @NotNull Class<T> clazz) {
|
||||
return of((Type) clazz);
|
||||
}
|
||||
|
||||
public static <T> ValueType<List<T>> ofList(final @NotNull Class<T> paramType) {
|
||||
return of(List.class, paramType);
|
||||
}
|
||||
|
||||
public static <T> ValueType<List<T>> ofList(final @NotNull ValueType<T> paramType) {
|
||||
return of(List.class, paramType.getType());
|
||||
}
|
||||
|
||||
public static <K, V> ValueType<Map<K, V>> ofMap(final @NotNull Class<K> keyType, final @NotNull Class<V> valueType) {
|
||||
return of(Map.class, keyType, valueType);
|
||||
}
|
||||
|
||||
public static <K, V> ValueType<Map<K, V>> ofMap(final @NotNull ValueType<K> keyType, final @NotNull ValueType<V> valueType) {
|
||||
return of(Map.class, keyType.getType(), valueType.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generic type of the complex type.
|
||||
*
|
||||
* @param rawType The raw type
|
||||
* @param types The type arguments
|
||||
* @param <T> The type
|
||||
* @return The {@link ValueType}
|
||||
*/
|
||||
public static <T> ValueType<T> of(final Class<?> rawType, final Type... types) {
|
||||
ParameterizedType parameterizedType = new ParameterizedType() {
|
||||
@Override
|
||||
public @NotNull Type @NotNull [] getActualTypeArguments() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return of(parameterizedType);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
|
||||
return new ValueType<T>(clazz) {
|
||||
};
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
protected ValueType() {
|
||||
this.type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private ValueType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this ValueType is a subtype of the given Class.
|
||||
*
|
||||
* @param target The target Class to check against
|
||||
* @return true if this ValueType is a subtype of the target Class, false otherwise
|
||||
*/
|
||||
public boolean isSubtypeOf(Class<?> target) {
|
||||
Class<?> rawType = getRawType();
|
||||
return target.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this ValueType is a subtype of the given ValueType.
|
||||
*
|
||||
* @param target The target ValueType to check against
|
||||
* @return true if this ValueType is a subtype of the target, false otherwise
|
||||
*/
|
||||
public boolean isSubtypeOf(ValueType<?> target) {
|
||||
return target.isSubtypeOf(getRawType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given object is an instance of the type represented by this ValueType.
|
||||
*
|
||||
* @param obj The object to check
|
||||
* @return true if the object is an instance of the type, false otherwise
|
||||
*/
|
||||
public boolean isInstance(Object obj) {
|
||||
return obj != null && getRawType().isInstance(obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the raw type from the generic type.
|
||||
*
|
||||
* @return The raw type of the generic type
|
||||
* @throws IllegalStateException if the type is not a Class or ParameterizedType
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<T> getRawType() {
|
||||
if (type instanceof Class<?>) {
|
||||
return (Class<T>) type;
|
||||
}
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) type;
|
||||
Type raw = pt.getRawType();
|
||||
if (raw instanceof Class<?>) {
|
||||
return (Class<T>) raw;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the object to the type represented by this ValueType.
|
||||
*
|
||||
* @param obj The object to cast
|
||||
* @return The object cast to the type represented by this ValueType
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T cast(Object obj) {
|
||||
if (!isInstance(obj)) {
|
||||
throw new ClassCastException("Cannot cast object " + obj + " to type " + this);
|
||||
}
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the type.
|
||||
* Like "{@code java.util.List<java.lang.String>}" or "java.lang.Integer".
|
||||
*
|
||||
* @return String representation of the type
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (type instanceof Class<?>) {
|
||||
return ((Class<?>) type).getName();
|
||||
}
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) type;
|
||||
Type raw = pt.getRawType();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(raw.getClass().getName());
|
||||
sb.append('<');
|
||||
Type[] args = pt.getActualTypeArguments();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(args[i].getTypeName());
|
||||
}
|
||||
sb.append('>');
|
||||
return sb.toString();
|
||||
}
|
||||
return type.getTypeName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof ValueType) {
|
||||
return Objects.equals(type, ((ValueType<?>) obj).type);
|
||||
}
|
||||
if (obj instanceof Type) {
|
||||
return Objects.equals(type, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package cc.carm.lib.configuration.adapter.strandard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PrimitiveAdapter<T> extends ValueAdapter<T> {
|
||||
|
||||
public static final String[] TRUE_VALUES = new String[]{
|
||||
"true", "yes", "on", "1", "enabled", "enable", "active"
|
||||
};
|
||||
|
||||
public static final String[] FALSE_VALUES = new String[]{
|
||||
"false", "no", "off", "0", "disabled", "disable", "inactive"
|
||||
};
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static ValueAdapter<Enum<?>> ofEnum() {
|
||||
ValueAdapter<Enum<?>> adapter = new ValueAdapter<>(new ValueType<Enum<?>>() {
|
||||
});
|
||||
adapter.parser((provider, type, data) -> Enum.valueOf((Class<Enum>) type.getRawType(), data.toString()));
|
||||
adapter.serializer((provider, type, value) -> value.name());
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<String> ofString() {
|
||||
return of(String.class, o -> o instanceof String ? (String) o : o.toString());
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Boolean> ofBoolean() {
|
||||
return of(Boolean.class, data -> {
|
||||
if (data instanceof Boolean) return (Boolean) data;
|
||||
String v = data.toString().trim();
|
||||
if (Arrays.stream(TRUE_VALUES).anyMatch(v::equalsIgnoreCase)) return true;
|
||||
else if (Arrays.stream(FALSE_VALUES).anyMatch(v::equalsIgnoreCase)) return false;
|
||||
else throw new IllegalArgumentException("Cannot parse boolean from " + data);
|
||||
});
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Boolean> ofBooleanType() {
|
||||
return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString()));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Character> ofCharacter() {
|
||||
return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Character> ofCharacterType() {
|
||||
return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Integer> ofInteger() {
|
||||
return ofNumber(Integer.class, Number::intValue, Integer::parseInt);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Integer> ofIntegerType() {
|
||||
return ofNumber(int.class, Number::intValue, Integer::parseInt);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Long> ofLong() {
|
||||
return ofNumber(Long.class, Number::longValue, Long::parseLong);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Long> ofLongType() {
|
||||
return ofNumber(long.class, Number::longValue, Long::parseLong);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Double> ofDouble() {
|
||||
return ofNumber(Double.class, Number::doubleValue, Double::parseDouble);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Double> ofDoubleType() {
|
||||
return ofNumber(double.class, Number::doubleValue, Double::parseDouble);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Float> ofFloat() {
|
||||
return ofNumber(Float.class, Number::floatValue, Float::parseFloat);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Float> ofFloatType() {
|
||||
return ofNumber(float.class, Number::floatValue, Float::parseFloat);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Short> ofShort() {
|
||||
return ofNumber(Short.class, Number::shortValue, Short::parseShort);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Short> ofShortType() {
|
||||
return ofNumber(short.class, Number::shortValue, Short::parseShort);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Byte> ofByte() {
|
||||
return ofNumber(Byte.class, Number::byteValue, Byte::parseByte);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Byte> ofByteType() {
|
||||
return ofNumber(byte.class, Number::byteValue, Byte::parseByte);
|
||||
}
|
||||
|
||||
public static <T> PrimitiveAdapter<T> of(@NotNull Class<T> clazz,
|
||||
@NotNull DataFunction<Object, T> function) {
|
||||
return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data));
|
||||
}
|
||||
|
||||
public static <T extends Number> PrimitiveAdapter<T> ofNumber(@NotNull Class<T> numberClass,
|
||||
@NotNull DataFunction<Number, T> castFunction,
|
||||
@NotNull DataFunction<String, T> parseFunction) {
|
||||
return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString()));
|
||||
}
|
||||
|
||||
protected PrimitiveAdapter(@NotNull Class<T> valueType, @NotNull ValueParser<T> deserializer) {
|
||||
super(ValueType.of(valueType), (provider, type, value) -> value, deserializer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cc.carm.lib.configuration.adapter.strandard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*;
|
||||
|
||||
public interface StandardAdapters {
|
||||
|
||||
@NotNull PrimitiveAdapter<?>[] PRIMITIVES = new PrimitiveAdapter[]{
|
||||
ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(),
|
||||
ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(),
|
||||
ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType()
|
||||
};
|
||||
|
||||
@NotNull ValueAdapter<Enum<?>> ENUMS = PrimitiveAdapter.ofEnum();
|
||||
|
||||
@NotNull ValueAdapter<UUID> UUID = new ValueAdapter<>(
|
||||
ValueType.of(UUID.class),
|
||||
(provider, type, value) -> value.toString(),
|
||||
(provider, type, value) -> java.util.UUID.fromString(value.toString())
|
||||
);
|
||||
|
||||
@NotNull ValueAdapter<ConfigureSection> SECTIONS = new ValueAdapter<>(
|
||||
ValueType.of(ConfigureSection.class),
|
||||
(provider, type, value) -> value,
|
||||
(provider, type, value) -> {
|
||||
if (value instanceof ConfigureSection) {
|
||||
return (ConfigureSection) value;
|
||||
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cc.carm.lib.configuration.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* The configuration path used to mark the corresponding class or parameter.
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigPath {
|
||||
|
||||
/**
|
||||
* The path value of the current configuration.
|
||||
* If not set,will generate the path by {@link cc.carm.lib.configuration.source.loader.PathGenerator}.
|
||||
*
|
||||
* @return The path value of the current configuration
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Whether to start with the root node.
|
||||
* <br>If false, the previous path (if any) to the node is automatically added.
|
||||
* <br>If true, the path will be set directly from the root node.
|
||||
*
|
||||
* @return Whether to start directly from the root path without inheriting the previous path
|
||||
*/
|
||||
boolean root() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package cc.carm.lib.configuration.builder;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.DataValidator;
|
||||
import cc.carm.lib.configuration.function.ValueValidator;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class AbstractConfigBuilder<
|
||||
TYPE, UNIT, RESULT extends ConfigValue<TYPE, UNIT>, HOLDER extends ConfigurationHolder<?>,
|
||||
SELF extends AbstractConfigBuilder<TYPE, UNIT, RESULT, HOLDER, SELF>
|
||||
> {
|
||||
|
||||
protected final Class<? super HOLDER> providerClass;
|
||||
protected final ValueType<TYPE> type;
|
||||
|
||||
protected @Nullable HOLDER holder;
|
||||
protected @Nullable String path;
|
||||
|
||||
protected @NotNull ValueValidator<UNIT> valueValidator = ValueValidator.none();
|
||||
protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null;
|
||||
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> {
|
||||
};
|
||||
|
||||
protected AbstractConfigBuilder(Class<? super HOLDER> providerClass, ValueType<TYPE> type) {
|
||||
this.providerClass = providerClass;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<TYPE> type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
protected abstract SELF self();
|
||||
|
||||
public abstract @NotNull RESULT build();
|
||||
|
||||
public SELF holder(@Nullable HOLDER holder) {
|
||||
this.holder = holder;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF path(@Nullable String path) {
|
||||
this.path = path;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ValueValidator} for the value.
|
||||
*
|
||||
* @param validator The validator to set.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validator(@NotNull ValueValidator<UNIT> validator) {
|
||||
this.valueValidator = validator;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link DataValidator} for the value.
|
||||
*
|
||||
* @param validator The validator to set.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validator(@NotNull DataValidator<? super UNIT> validator) {
|
||||
return validator((h, value) -> validator.validate(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the value with the specified condition.
|
||||
*
|
||||
* @param validator The validator to append.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validate(@NotNull ValueValidator<? super UNIT> validator) {
|
||||
return validator(this.valueValidator.and(validator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the value with the specified condition.
|
||||
*
|
||||
* @param validator The validator to append.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validate(@NotNull DataValidator<? super UNIT> validator) {
|
||||
return validate((h, value) -> validator.validate(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the value with the specified condition.
|
||||
*
|
||||
* @param condition The condition to check, if the condition is false, an exception will be thrown.
|
||||
* @param exception The exception to throw if the condition is false.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull Exception exception) {
|
||||
return validate((h, value) -> {
|
||||
if (!condition.test(value)) throw exception;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the value with the specified condition.
|
||||
*
|
||||
* @param condition The condition to check, if the condition is false, an exception will be thrown.
|
||||
* @param msg The message to throw if the condition is false.
|
||||
* @return this builder
|
||||
*/
|
||||
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull String msg) {
|
||||
return validate((h, value) -> {
|
||||
if (!condition.test(value)) throw new IllegalArgumentException(msg);
|
||||
});
|
||||
}
|
||||
|
||||
public SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||
this.initializer = initializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||
return initializer(initializer.andThen(initializer));
|
||||
}
|
||||
|
||||
public SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
|
||||
return append((provider, valuePath) -> initializer.accept(provider));
|
||||
}
|
||||
|
||||
public SELF defaults(@Nullable TYPE defaultValue) {
|
||||
return defaults(() -> defaultValue);
|
||||
}
|
||||
|
||||
public SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
|
||||
this.defaultValueSupplier = supplier;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
|
||||
return append((h, p) -> metaConsumer.accept(h.metadata(p)));
|
||||
}
|
||||
|
||||
public <M> SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
|
||||
return meta(h -> h.set(type, value));
|
||||
}
|
||||
|
||||
protected @NotNull ValueManifest<TYPE, UNIT> buildManifest() {
|
||||
return new ValueManifest<>(
|
||||
type(), this.defaultValueSupplier, this.valueValidator,
|
||||
this.initializer, this.holder, this.path
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cc.carm.lib.configuration.builder;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
|
||||
public abstract class CommonConfigBuilder<
|
||||
TYPE, UNIT,
|
||||
RESULT extends ConfigValue<TYPE, UNIT>,
|
||||
SELF extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF>
|
||||
> extends AbstractConfigBuilder<TYPE, UNIT, RESULT, ConfigurationHolder<?>, SELF> {
|
||||
|
||||
protected CommonConfigBuilder(ValueType<TYPE> type) {
|
||||
super(ConfigurationHolder.class, type);
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package cc.carm.lib.configuration.builder.collection;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class SectionCollectionBuilder<
|
||||
V, C extends Collection<V>,
|
||||
RESULT extends CollectionConfigValue<V, C, ?>,
|
||||
SELF extends SectionCollectionBuilder<V, C, RESULT, SELF>
|
||||
> extends AbstractSectionBuilder<C, V, RESULT, SELF> {
|
||||
|
||||
protected @NotNull Supplier<? extends C> constructor;
|
||||
|
||||
public SectionCollectionBuilder(@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||
super(new ValueType<C>() {
|
||||
}, paramType, parser, serializer);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final @NotNull SELF defaults(@NotNull V... values) {
|
||||
return defaults(c -> c.addAll(Arrays.asList(values)));
|
||||
}
|
||||
|
||||
public final @NotNull SELF defaults(@NotNull Consumer<C> constructor) {
|
||||
return defaults(() -> {
|
||||
C collection = this.constructor.get();
|
||||
constructor.accept(collection);
|
||||
return collection;
|
||||
});
|
||||
}
|
||||
|
||||
public SELF constructor(@NotNull Supplier<? extends C> constructor) {
|
||||
this.constructor = constructor;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF construct(@NotNull C collection) {
|
||||
return constructor(() -> collection);
|
||||
}
|
||||
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package cc.carm.lib.configuration.builder.collection;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SimpleCollectionCreator<V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>> {
|
||||
|
||||
public static <V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
|
||||
@NotNull SimpleCollectionCreator<V, C, RESULT> create(
|
||||
@NotNull ValueType<V> type,
|
||||
@NotNull Supplier<? extends C> defaultConstructor,
|
||||
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
|
||||
return new SimpleCollectionCreator<>(type, defaultConstructor, factory);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends C> defaultConstructor;
|
||||
protected final @NotNull ValueType<V> type;
|
||||
|
||||
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
|
||||
|
||||
public SimpleCollectionCreator(@NotNull ValueType<V> type,
|
||||
@NotNull Supplier<? extends C> defaultConstructor,
|
||||
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
|
||||
this.defaultConstructor = defaultConstructor;
|
||||
this.type = type;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public <S> @NotNull Source<S, V, C, RESULT> from(@NotNull Class<S> sourceType) {
|
||||
return from(ValueType.of(sourceType));
|
||||
}
|
||||
|
||||
public <S> @NotNull Source<S, V, C, RESULT> from(@NotNull ValueType<S> sourceType) {
|
||||
return new Source<S, V, C, RESULT>(
|
||||
defaultConstructor, sourceType, type,
|
||||
ValueHandler.required(type),
|
||||
ValueHandler.required(sourceType),
|
||||
factory
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SimpleCollectionCreator.Source<Object, V, C, RESULT> fromObject() {
|
||||
return new Source<Object, V, C, RESULT>(
|
||||
defaultConstructor, ValueType.OBJECT, type,
|
||||
ValueHandler.deserialize(type), ValueHandler.toObject(),
|
||||
factory
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SimpleCollectionCreator.Source<String, V, C, RESULT> fromString() {
|
||||
return new Source<String, V, C, RESULT>(
|
||||
defaultConstructor, ValueType.STRING, type,
|
||||
ValueHandler.required(type), ValueHandler.stringValue(),
|
||||
factory
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SimpleCollectionCreator.Section<V, C, RESULT> fromSection() {
|
||||
return new Section<V, C, RESULT>(
|
||||
defaultConstructor, type,
|
||||
ValueHandler.required(type), ValueHandler.required(),
|
||||
factory
|
||||
);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CollectionValueFactory<V, C, RESULT> {
|
||||
@NotNull RESULT build(
|
||||
@NotNull ValueManifest<C, V> manifest,
|
||||
@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueAdapter<V> paramAdapter
|
||||
);
|
||||
}
|
||||
|
||||
public static class Source<SOURCE, V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
|
||||
extends SourceCollectionBuilder<SOURCE, V, C, RESULT, Source<SOURCE, V, C, RESULT>> {
|
||||
|
||||
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
|
||||
|
||||
public Source(
|
||||
@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueType<SOURCE> sourceType,
|
||||
@NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<SOURCE, V> parser,
|
||||
@NotNull ValueHandler<V, SOURCE> serializer,
|
||||
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
|
||||
super(constructor, sourceType, paramType, parser, serializer);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SimpleCollectionCreator.Source<SOURCE, V, C, RESULT> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RESULT build() {
|
||||
return factory.build(buildManifest(), constructor, buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Section<V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
|
||||
extends SectionCollectionBuilder<V, C, RESULT, Section<V, C, RESULT>> {
|
||||
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
|
||||
|
||||
public Section(
|
||||
@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer,
|
||||
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
|
||||
super(constructor, paramType, parser, serializer);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SimpleCollectionCreator.Section<V, C, RESULT> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RESULT build() {
|
||||
return factory.build(buildManifest(), constructor, buildAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package cc.carm.lib.configuration.builder.collection;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class SourceCollectionBuilder<
|
||||
SOURCE, V, C extends Collection<V>,
|
||||
RESULT extends CollectionConfigValue<V, C, ?>,
|
||||
SELF extends SourceCollectionBuilder<SOURCE, V, C, RESULT, SELF>
|
||||
>
|
||||
extends AbstractSourceBuilder<C, SOURCE, V, RESULT, SELF> {
|
||||
|
||||
protected @NotNull Supplier<? extends C> constructor;
|
||||
|
||||
public SourceCollectionBuilder(@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer) {
|
||||
super(new ValueType<C>() {
|
||||
}, sourceType, paramType, parser, serializer);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final @NotNull SELF defaults(@NotNull V... values) {
|
||||
return defaults(c -> c.addAll(Arrays.asList(values)));
|
||||
}
|
||||
|
||||
public final @NotNull SELF defaults(@NotNull Consumer<C> constructor) {
|
||||
return defaults(() -> {
|
||||
C collection = this.constructor.get();
|
||||
constructor.accept(collection);
|
||||
return collection;
|
||||
});
|
||||
}
|
||||
|
||||
public SELF constructor(@NotNull Supplier<? extends C> constructor) {
|
||||
this.constructor = constructor;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF construct(@NotNull C collection) {
|
||||
return constructor(() -> collection);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package cc.carm.lib.configuration.builder.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueComposer;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractSectionBuilder<
|
||||
TYPE, UNIT,
|
||||
RESULT extends ConfigValue<TYPE, UNIT>,
|
||||
SELF extends AbstractSectionBuilder<TYPE, UNIT, RESULT, SELF>
|
||||
> extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF> {
|
||||
|
||||
|
||||
protected final @NotNull ValueType<UNIT> paramType;
|
||||
|
||||
protected @NotNull ValueHandler<ConfigureSection, UNIT> parser;
|
||||
protected @NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer;
|
||||
|
||||
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<UNIT> paramType,
|
||||
@NotNull ValueHandler<ConfigureSection, UNIT> parser,
|
||||
@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
|
||||
super(type);
|
||||
this.paramType = paramType;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, UNIT> valueParser) {
|
||||
return parse((p, section) -> valueParser.handle(section));
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, UNIT> valueParser) {
|
||||
this.parser = valueParser;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
|
||||
this.serializer = serializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, ? extends Map<String, Object>> serializer) {
|
||||
return serialize((p, value) -> serializer.handle(value));
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull ValueComposer<Map<String, Object>, UNIT> serializer) {
|
||||
return serialize((h, value) -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
serializer.accept(h, map, value);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
protected ValueAdapter<UNIT> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType)
|
||||
.parser((p, type, data) -> {
|
||||
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
|
||||
if (section == null) return null;
|
||||
return this.parser.handle(p, section);
|
||||
})
|
||||
.serializer((p, type, data) -> {
|
||||
Map<String, Object> map = this.serializer.handle(p, data);
|
||||
return map == null || map.isEmpty() ? null : map;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package cc.carm.lib.configuration.builder.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractSourceBuilder<
|
||||
V, SOURCE, UNIT, RESULT extends ConfigValue<V, UNIT>,
|
||||
SELF extends AbstractSourceBuilder<V, SOURCE, UNIT, RESULT, SELF>
|
||||
> extends CommonConfigBuilder<V, UNIT, RESULT, SELF> {
|
||||
|
||||
protected final @NotNull ValueType<SOURCE> sourceType;
|
||||
protected final @NotNull ValueType<UNIT> paramType;
|
||||
|
||||
@SuppressWarnings("NotNullFieldNotInitialized") // Already initialized in constructor
|
||||
protected @NotNull ValueParser<UNIT> valueParser;
|
||||
@SuppressWarnings("NotNullFieldNotInitialized") // Already initialized in constructor
|
||||
protected @NotNull ValueSerializer<UNIT> valueSerializer;
|
||||
|
||||
protected AbstractSourceBuilder(@NotNull ValueType<V> type,
|
||||
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<UNIT> paramType,
|
||||
@NotNull ValueHandler<SOURCE, UNIT> parser,
|
||||
@NotNull ValueHandler<UNIT, SOURCE> serializer) {
|
||||
super(type);
|
||||
this.sourceType = sourceType;
|
||||
this.paramType = paramType;
|
||||
parse(parser);
|
||||
serialize(serializer);
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, UNIT> parser) {
|
||||
return parse((p, source) -> parser.handle(source));
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, UNIT> parser) {
|
||||
return parser((holder, type, data) -> {
|
||||
SOURCE source = holder.deserialize(this.sourceType, data);
|
||||
return parser.handle(holder, source);
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull SELF parser(@NotNull ValueParser<UNIT> parser) {
|
||||
this.valueParser = parser;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, SOURCE> serializer) {
|
||||
return serializer((holder, type, data) -> {
|
||||
SOURCE source = serializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, SOURCE> serializer) {
|
||||
return serialize((p, value) -> serializer.handle(value));
|
||||
}
|
||||
|
||||
public @NotNull SELF serializer(@NotNull ValueSerializer<UNIT> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
protected ValueAdapter<UNIT> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType, this.valueSerializer, this.valueParser);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cc.carm.lib.configuration.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.collection.SimpleCollectionCreator;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigListCreator<V> extends SimpleCollectionCreator<V, List<V>, ConfiguredList<V>> {
|
||||
|
||||
public ConfigListCreator(@NotNull ValueType<V> type) {
|
||||
super(type, ArrayList::new, ConfiguredList::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
||||
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> constructor;
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
protected final @NotNull ValueType<V> valueType;
|
||||
|
||||
public ConfigMapBuilder(@NotNull Supplier<@NotNull M> constructor,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
this.constructor = constructor;
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public <W extends Map<K, V>> ConfigMapBuilder<W, K, V> constructor(@NotNull Supplier<W> supplier) {
|
||||
return new ConfigMapBuilder<>(supplier, keyType, valueType);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> clazz) {
|
||||
return from(ValueType.of(clazz));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceMapBuilder<M, S, K, V> from(@NotNull ValueType<S> sourceType) {
|
||||
return from(
|
||||
sourceType,
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.required()
|
||||
);
|
||||
}
|
||||
|
||||
public <S> @NotNull SourceMapBuilder<M, S, K, V> from(@NotNull ValueType<S> sourceType,
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<S, V> valueParser,
|
||||
@NotNull ValueHandler<V, S> valueSerializer) {
|
||||
return new SourceMapBuilder<>(
|
||||
this.constructor, sourceType, keyType, valueType,
|
||||
keyParser, keySerializer, valueParser, valueSerializer
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public @NotNull <S> SourceMapBuilder<M, Object, K, V> fromObject() {
|
||||
return from(
|
||||
ValueType.OBJECT,
|
||||
ValueHandler.deserialize(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.deserialize(valueType), ValueHandler.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, String, K, V> fromString() {
|
||||
return from(
|
||||
ValueType.STRING,
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.stringValue()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<M, K, V> fromSection() {
|
||||
return fromSection(
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<M, K, V> fromSection(
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer
|
||||
) {
|
||||
return new SectionMapBuilder<>(
|
||||
this.constructor, keyType, valueType,
|
||||
keyParser, keySerializer, valueParser, valueSerializer
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapCreator<K, V> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
protected final @NotNull ValueType<V> valueType;
|
||||
|
||||
public ConfigMapCreator(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public <M extends Map<K, V>> @NotNull ConfigMapBuilder<M, K, V> constructor(@NotNull Supplier<@NotNull M> mapSuppler) {
|
||||
return new ConfigMapBuilder<>(mapSuppler, keyType, valueType);
|
||||
}
|
||||
|
||||
public <W extends Map<K, V>> @NotNull ConfigMapBuilder<W, K, V> constructor(@NotNull Class<W> type) {
|
||||
return constructor(() -> {
|
||||
try {
|
||||
return type.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap() {
|
||||
return constructor(HashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap() {
|
||||
return constructor(LinkedHashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap() {
|
||||
return constructor(TreeMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Comparator<? super K> comparator) {
|
||||
return constructor(() -> new TreeMap<>(comparator));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SectionMapBuilder<MAP extends Map<K, V>, K, V>
|
||||
extends AbstractSectionBuilder<Map<K, V>, V, ConfiguredMap<K, V>, SectionMapBuilder<MAP, K, V>> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
|
||||
protected @NotNull Supplier<? extends MAP> constructor;
|
||||
protected @NotNull ValueHandler<String, K> keyParser;
|
||||
protected @NotNull ValueHandler<K, String> keySerializer;
|
||||
|
||||
public SectionMapBuilder(@NotNull Supplier<? extends MAP> constructor,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer) {
|
||||
super(new ValueType<Map<K, V>>() {
|
||||
}, valueType, valueParser, valueSerializer);
|
||||
this.keyType = keyType;
|
||||
this.constructor = constructor;
|
||||
this.keyParser = keyParser;
|
||||
this.keySerializer = keySerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionMapBuilder<MAP, K, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> parseKey(@NotNull DataFunction<String, K> keyParser) {
|
||||
return parseKey((holder, data) -> keyParser.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> parseKey(@NotNull ValueHandler<String, K> keyParser) {
|
||||
this.keyParser = keyParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> serializeKey(@NotNull DataFunction<K, String> keySerializer) {
|
||||
return serializeKey((holder, data) -> keySerializer.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> serializeKey(@NotNull ValueHandler<K, String> keySerializer) {
|
||||
this.keySerializer = keySerializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull MAP defaults) {
|
||||
return defaults(() -> defaults);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull Consumer<MAP> defaults) {
|
||||
return defaults(() -> {
|
||||
MAP map = this.constructor.get();
|
||||
defaults.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull K key, @NotNull V value) {
|
||||
return defaults(map -> map.put(key, value));
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> buildKeyAdapter() {
|
||||
return new ValueAdapter<>(this.keyType)
|
||||
.parser((holder, type, data) -> {
|
||||
String source = holder.deserialize(String.class, data);
|
||||
return this.keyParser.handle(holder, source);
|
||||
})
|
||||
.serializer((holder, type, data) -> {
|
||||
String source = this.keySerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(buildManifest(), this.constructor, buildKeyAdapter(), this.buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
|
||||
extends AbstractSourceBuilder<Map<K, V>, SOURCE, V, ConfiguredMap<K, V>, SourceMapBuilder<MAP, SOURCE, K, V>> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
|
||||
protected @NotNull Supplier<? extends MAP> constructor;
|
||||
protected @NotNull ValueHandler<String, K> keyParser;
|
||||
protected @NotNull ValueHandler<K, String> keySerializer;
|
||||
|
||||
public SourceMapBuilder(@NotNull Supplier<? extends MAP> constructor, @NotNull ValueType<SOURCE> sourceType,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<String, K> keyParser, @NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<SOURCE, V> valueParser, @NotNull ValueHandler<V, SOURCE> valueSerializer) {
|
||||
super(new ValueType<Map<K, V>>() {
|
||||
}, sourceType, valueType, valueParser, valueSerializer);
|
||||
this.keyType = keyType;
|
||||
this.constructor = constructor;
|
||||
this.keyParser = keyParser;
|
||||
this.keySerializer = keySerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceMapBuilder<MAP, SOURCE, K, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull MAP defaults) {
|
||||
return defaults(() -> defaults);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull Consumer<MAP> defaults) {
|
||||
return defaults(() -> {
|
||||
MAP map = this.constructor.get();
|
||||
defaults.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull K key, @NotNull V value) {
|
||||
return defaults(map -> map.put(key, value));
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> parseKey(@NotNull DataFunction<String, K> keyParser) {
|
||||
return parseKey((holder, data) -> keyParser.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> parseKey(@NotNull ValueHandler<String, K> keyParser) {
|
||||
this.keyParser = keyParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> serializeKey(@NotNull DataFunction<K, String> keySerializer) {
|
||||
return serializeKey((holder, data) -> keySerializer.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> serializeKey(@NotNull ValueHandler<K, String> keySerializer) {
|
||||
this.keySerializer = keySerializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> buildKeyAdapter() {
|
||||
return new ValueAdapter<>(this.keyType)
|
||||
.parser((holder, type, data) -> {
|
||||
String source = holder.deserialize(String.class, data);
|
||||
return this.keyParser.handle(holder, source);
|
||||
})
|
||||
.serializer((holder, type, data) -> {
|
||||
String source = this.keySerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(buildManifest(), this.constructor, buildKeyAdapter(), this.buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigValueBuilder<V> {
|
||||
|
||||
protected final @NotNull ValueType<V> type;
|
||||
|
||||
public ConfigValueBuilder(@NotNull ValueType<V> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> clazz) {
|
||||
return from(ValueType.of(clazz));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
|
||||
return from(sourceType, ValueHandler.required(type), ValueHandler.required(sourceType));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType,
|
||||
@NotNull ValueHandler<S, V> valueParser,
|
||||
@NotNull ValueHandler<V, S> valueSerializer) {
|
||||
return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||
return from(ValueType.STRING, ValueHandler.required(type), ValueHandler.stringValue());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||
return fromSection(ValueHandler.required(type), ValueHandler.required());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
|
||||
) {
|
||||
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionValueBuilder<V> extends AbstractSectionBuilder<V, V, ConfiguredValue<V>, SectionValueBuilder<V>> {
|
||||
|
||||
public SectionValueBuilder(@NotNull ValueType<V> type,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||
super(type, type, parser, serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionValueBuilder<V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return ConfiguredValue.of(buildManifest(), buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SourceValueBuilder<S, V>
|
||||
extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> {
|
||||
|
||||
|
||||
public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<S, V> parser, @NotNull ValueHandler<V, S> serializer) {
|
||||
super(valueType, sourceType, valueType, parser, serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceValueBuilder<S, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return new ConfiguredValue<>(buildManifest(), buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigComment;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfigInitializer {
|
||||
|
||||
public static void initialize(ConfigurationProvider source, Class<? extends ConfigurationRoot> rootClazz) {
|
||||
initialize(source, rootClazz, true);
|
||||
}
|
||||
|
||||
public static void initialize(ConfigurationProvider provider, Class<? extends ConfigurationRoot> rootClazz, boolean saveDefault) {
|
||||
ConfigPath sectionAnnotation = rootClazz.getAnnotation(ConfigPath.class);
|
||||
|
||||
String rootSection = null;
|
||||
if (sectionAnnotation != null && sectionAnnotation.value().length() > 1) {
|
||||
rootSection = sectionAnnotation.value();
|
||||
}
|
||||
|
||||
for (Class<?> innerClass : rootClazz.getDeclaredClasses()) {
|
||||
initSection(provider, rootSection, innerClass, saveDefault);
|
||||
}
|
||||
|
||||
for (Field field : rootClazz.getFields()) {
|
||||
initValue(provider, rootSection, rootClazz, field, saveDefault);
|
||||
}
|
||||
|
||||
if (saveDefault) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void initSection(ConfigurationProvider provider, String parentSection, Class<?> clazz, boolean saveDefault) {
|
||||
if (!Modifier.isStatic(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers())) return;
|
||||
|
||||
String section = getSectionPath(clazz.getSimpleName(), parentSection, clazz.getAnnotation(ConfigPath.class));
|
||||
ConfigComment comments = clazz.getAnnotation(ConfigComment.class);
|
||||
if (comments != null && comments.value().length > 0) {
|
||||
provider.setComments(parentSection, comments.value());
|
||||
}
|
||||
|
||||
for (Field field : clazz.getFields()) initValue(provider, section, clazz, field, saveDefault);
|
||||
for (Class<?> innerClass : clazz.getDeclaredClasses()) initSection(provider, section, innerClass, saveDefault);
|
||||
|
||||
}
|
||||
|
||||
private static void initValue(ConfigurationProvider provider, String parentSection, Class<?> clazz, Field field, boolean saveDefault) {
|
||||
try {
|
||||
Object object = field.get(clazz);
|
||||
if (object instanceof ConfigValue<?>) {
|
||||
initializeValue(
|
||||
provider, (ConfigValue<?>) object,
|
||||
getSectionPath(field.getName(), parentSection, field.getAnnotation(ConfigPath.class)),
|
||||
Optional.ofNullable(field.getAnnotation(ConfigComment.class))
|
||||
.map(ConfigComment::value).orElse(new String[0]),
|
||||
saveDefault);
|
||||
}
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void initializeValue(@NotNull ConfigurationProvider provider, @NotNull ConfigValue<?> value,
|
||||
@NotNull String path, @NotNull String[] comments, boolean saveDefault) {
|
||||
value.initialize(provider, path, comments);
|
||||
if (saveDefault && value.getDefaultValue() != null && !provider.getConfiguration().contains(path)) {
|
||||
value.setDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSectionPath(@NotNull String name,
|
||||
@Nullable String parentSection,
|
||||
@Nullable ConfigPath pathAnnotation) {
|
||||
String parent = parentSection != null ? parentSection + "." : "";
|
||||
if (pathAnnotation != null && pathAnnotation.value().length() > 0) {
|
||||
return parent + pathAnnotation.value();
|
||||
} else {
|
||||
return parent + getSectionName(name);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSectionName(String codeName) {
|
||||
return codeName.toLowerCase().replace("_", "-");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
public abstract class ConfigurationRoot {
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.annotation;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigComment {
|
||||
|
||||
@NotNull
|
||||
String[] value() default "";
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractConfigBuilder<B extends AbstractConfigBuilder<B, T>, T> {
|
||||
|
||||
|
||||
protected @Nullable ConfigurationProvider provider;
|
||||
protected @Nullable String path;
|
||||
|
||||
protected @NotNull String[] comments = new String[0];
|
||||
protected @Nullable T defaultValue;
|
||||
|
||||
protected abstract @NotNull B getThis();
|
||||
|
||||
public abstract @NotNull ConfigValue<?> build();
|
||||
|
||||
public @NotNull B from(@Nullable ConfigurationProvider provider) {
|
||||
this.provider = provider;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B path(@Nullable String path) {
|
||||
this.path = path;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B comments(@NotNull String... comments) {
|
||||
this.comments = comments;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B defaults(@Nullable T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ConfigBuilder {
|
||||
|
||||
public <V> @NotNull ConfigValueBuilder<V> asValue(@NotNull Class<V> valueClass) {
|
||||
return new ConfigValueBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
public <V> @NotNull ConfigListBuilder<V> asList(@NotNull Class<V> valueClass) {
|
||||
return new ConfigListBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return new ConfigMapBuilder<>(LinkedHashMap::new, keyClass, valueClass);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass).supplier(HashMap::new);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass);
|
||||
}
|
||||
|
||||
public <K extends Comparable<K>, V> @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass).supplier(TreeMap::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ConfigListBuilder<V> {
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigListBuilder(@NotNull Class<V> valueClass) {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceListBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
return new SourceListBuilder<>(sourceClass, sourceParser, this.valueClass, valueParser, valueSerializer, sourceSerializer);
|
||||
}
|
||||
|
||||
|
||||
public @NotNull <S> SourceListBuilder<S, V> from(Class<S> sourceClass) {
|
||||
return from(sourceClass,
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required(),
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<Object, V> fromObject() {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigDataFunction.castObject(valueClass),
|
||||
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<String, V> fromString() {
|
||||
return from(
|
||||
String.class, ConfigDataFunction.castToString(),
|
||||
ConfigDataFunction.castFromString(this.valueClass),
|
||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SourceListBuilder<S, V>
|
||||
extends AbstractConfigBuilder<SourceListBuilder<S, V>, List<V>> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
|
||||
public SourceListBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> parseSource(ConfigDataFunction<Object, S> sourceParser) {
|
||||
this.sourceParser = sourceParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> parseValue(ConfigDataFunction<S, V> valueParser) {
|
||||
this.valueParser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> serializeValue(ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> serializeSource(ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceListBuilder<S, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> build() {
|
||||
return new ConfiguredList<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
this.sourceParser.andThen(this.valueParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigMapBuilder(@NotNull Supplier<@NotNull M> supplier, @NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public <MAP extends Map<K, V>> ConfigMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
||||
return new ConfigMapBuilder<>(supplier, keyClass, valueClass);
|
||||
}
|
||||
|
||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer) {
|
||||
return new SourceMapBuilder<>(supplier,
|
||||
keyClass, ConfigDataFunction.castFromString(this.keyClass), // #String -> key
|
||||
sourceClass, ConfigDataFunction.castObject(sourceClass), // #Object -> source
|
||||
valueClass, valueParser, // source -> value
|
||||
ConfigDataFunction.castToString(), // key -> #String
|
||||
valueSerializer/*value -> source*/,
|
||||
ConfigDataFunction.toObject()/* source -> #Object */
|
||||
);
|
||||
}
|
||||
|
||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass) {
|
||||
return from(sourceClass, ConfigDataFunction.required(), ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, String, K, V> fromString(@NotNull ConfigDataFunction<String, V> valueParser) {
|
||||
return from(String.class, valueParser, ConfigDataFunction.castToString());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, String, K, V> fromString() {
|
||||
return fromString(ConfigDataFunction.castFromString(this.valueClass));
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, Object, K, V> fromObject(@NotNull ConfigDataFunction<Object, V> valueParser) {
|
||||
return from(Object.class, valueParser, ConfigDataFunction.toObject());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, Object, K, V> fromObject() {
|
||||
return fromObject(ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SourceMapBuilder<M extends Map<K, V>, S, K, V>
|
||||
extends AbstractConfigBuilder<SourceMapBuilder<M, S, K, V>, M> {
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected @NotNull ConfigDataFunction<String, K> keyParser;
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<K, String> keySerializer;
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
|
||||
public SourceMapBuilder(@NotNull Supplier<@NotNull M> supplier,
|
||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
||||
@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.keyParser = keyParser;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.keySerializer = keySerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
}
|
||||
|
||||
public <MAP extends Map<K, V>> SourceMapBuilder<MAP, S, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
||||
return new SourceMapBuilder<>(supplier,
|
||||
keyClass, keyParser, sourceClass, sourceParser, valueClass, valueParser,
|
||||
keySerializer, valueSerializer, sourceSerializer
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
|
||||
this.keyParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseSource(@NotNull ConfigDataFunction<Object, S> parser) {
|
||||
this.sourceParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseValue(@NotNull ConfigDataFunction<S, V> parser) {
|
||||
this.valueParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
|
||||
this.keySerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceMapBuilder<M, S, K, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.defaultValue, this.supplier,
|
||||
this.keyClass, this.keyParser,
|
||||
this.valueClass, this.sourceParser.andThen(this.valueParser),
|
||||
this.keySerializer, this.valueSerializer.andThen(this.sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigValueBuilder<V> {
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigValueBuilder(@NotNull Class<V> valueClass) {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
||||
return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(Class<S> sourceClass) {
|
||||
return from(
|
||||
sourceClass, ConfigDataFunction.required(), ConfigValueParser.required(),
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull ConfigValueParser<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
return new SourceValueBuilder<>(
|
||||
sourceClass, sourceParser, this.valueClass, valueParser,
|
||||
valueSerializer, sourceSerializer
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<Object, V> fromObject() {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigValueParser.castObject(valueClass),
|
||||
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||
return from(
|
||||
String.class, ConfigDataFunction.castToString(),
|
||||
ConfigValueParser.parseString(this.valueClass),
|
||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionValueBuilder<V>
|
||||
extends AbstractConfigBuilder<SectionValueBuilder<V>, V> {
|
||||
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
|
||||
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public SectionValueBuilder(@NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionValueBuilder<V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper, V> valueParser) {
|
||||
this.parser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> serializeValue(ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
this.serializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredSection<V> build() {
|
||||
return new ConfiguredSection<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
this.parser, this.serializer
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SourceValueBuilder<S, V> extends AbstractConfigBuilder<SourceValueBuilder<S, V>, V> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigValueParser<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
|
||||
public SourceValueBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigValueParser<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceValueBuilder<S, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> parseSource(@NotNull ConfigDataFunction<Object, S> sourceParser) {
|
||||
this.sourceParser = sourceParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> parseValue(@NotNull ConfigValueParser<S, V> valueParser) {
|
||||
this.valueParser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return new ConfiguredValue<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
this.valueParser.compose(this.sourceParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.function;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigDataFunction<T, R> {
|
||||
|
||||
@NotNull R parse(@NotNull T data) throws Exception;
|
||||
|
||||
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((data) -> after.parse(parse(data)));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, ? super T> identity() {
|
||||
return (input) -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
|
||||
return (input) -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
|
||||
return (input) -> input;
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
|
||||
return (input) -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else return input.toString();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ConfigurationProvider {
|
||||
|
||||
protected long updateTime;
|
||||
|
||||
public ConfigurationProvider() {
|
||||
this.updateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public boolean isExpired(long time) {
|
||||
return this.updateTime > time;
|
||||
}
|
||||
|
||||
public abstract @NotNull ConfigurationWrapper getConfiguration();
|
||||
|
||||
public abstract void reload() throws Exception;
|
||||
|
||||
public abstract void save() throws Exception;
|
||||
|
||||
public abstract void setComments(@NotNull String path, @NotNull String... comments);
|
||||
|
||||
public abstract @Nullable String[] getComments(@NotNull String path);
|
||||
|
||||
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
|
||||
ConfigInitializer.initialize(this, configClazz, true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
interface ConfigurationReader {
|
||||
|
||||
ConfigurationWrapper getWrapper();
|
||||
|
||||
default boolean isBoolean(@NotNull String path) {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
default boolean getBoolean(@NotNull String path) {
|
||||
return getBoolean(path, false);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.booleanValue());
|
||||
}
|
||||
|
||||
default @Nullable Boolean isByte(@NotNull String path) {
|
||||
return getWrapper().isType(path, Byte.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Byte getByte(@NotNull String path) {
|
||||
return getByte(path, (byte) 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.byteValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isShort(@NotNull String path) {
|
||||
return getWrapper().isType(path, Short.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Short getShort(@NotNull String path) {
|
||||
return getShort(path, (short) 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.shortValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isInt(@NotNull String path) {
|
||||
return getWrapper().isType(path, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Integer getInt(@NotNull String path) {
|
||||
return getInt(path, 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.intValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isLong(@NotNull String path) {
|
||||
return getWrapper().isType(path, Long.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Long getLong(@NotNull String path) {
|
||||
return getLong(path, 0L);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.longValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isFloat(@NotNull String path) {
|
||||
return getWrapper().isType(path, Float.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Float getFloat(@NotNull String path) {
|
||||
return getFloat(path, 0.0F);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.floatValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isDouble(@NotNull String path) {
|
||||
return getWrapper().isType(path, Double.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Double getDouble(@NotNull String path) {
|
||||
return getDouble(path, 0.0D);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.doubleValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isChar(@NotNull String path) {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Character getChar(@NotNull String path) {
|
||||
return getChar(path, null);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||
return getWrapper().get(path, def, Character.class);
|
||||
}
|
||||
|
||||
|
||||
default boolean isString(@NotNull String path) {
|
||||
return getWrapper().isType(path, String.class);
|
||||
}
|
||||
|
||||
default @Nullable String getString(@NotNull String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||
return getWrapper().get(path, def, String.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConfigurationWrapper extends ConfigurationReader{
|
||||
|
||||
@Override
|
||||
default ConfigurationWrapper getWrapper() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Set<String> getKeys(boolean deep);
|
||||
|
||||
@NotNull
|
||||
Map<String, Object> getValues(boolean deep);
|
||||
|
||||
void set(@NotNull String path, @Nullable Object value);
|
||||
|
||||
boolean contains(@NotNull String path);
|
||||
|
||||
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||
return typeClass.isInstance(get(path));
|
||||
}
|
||||
|
||||
@Nullable Object get(@NotNull String path);
|
||||
|
||||
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> clazz) {
|
||||
return get(path, null, clazz);
|
||||
}
|
||||
|
||||
default @Nullable <T> T get(@NotNull String path, @NotNull ConfigValueParser<Object, T> parser) {
|
||||
return get(path, null, parser);
|
||||
}
|
||||
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
|
||||
return get(path, defaultValue, ConfigValueParser.castObject(clazz));
|
||||
}
|
||||
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||
@NotNull ConfigValueParser<Object, T> parser) {
|
||||
Object value = get(path);
|
||||
if (value != null) {
|
||||
try {
|
||||
return parser.parse(value, defaultValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
boolean isList(@NotNull String path);
|
||||
|
||||
@Nullable List<?> getList(@NotNull String path);
|
||||
|
||||
boolean isConfigurationSection(@NotNull String path);
|
||||
|
||||
@Nullable
|
||||
ConfigurationWrapper getConfigurationSection(@NotNull String path);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source.impl;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class FileConfigProvider extends ConfigurationProvider {
|
||||
|
||||
protected final @NotNull File file;
|
||||
|
||||
public FileConfigProvider(@NotNull File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public @NotNull File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void initializeFile(@Nullable String sourcePath) throws IOException {
|
||||
if (getFile().exists()) return;
|
||||
if (!getFile().getParentFile().exists() && !getFile().getParentFile().mkdirs()) {
|
||||
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
|
||||
}
|
||||
if (!getFile().createNewFile()) {
|
||||
throw new IOException("Failed to create file " + file.getAbsolutePath());
|
||||
}
|
||||
if (sourcePath == null) return;
|
||||
try {
|
||||
saveResource(sourcePath, true);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void saveResource(@NotNull String resourcePath, boolean replace)
|
||||
throws NullPointerException, IOException, IllegalArgumentException {
|
||||
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
||||
if (resourcePath.equals("")) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
||||
|
||||
resourcePath = resourcePath.replace('\\', '/');
|
||||
InputStream in = getResource(resourcePath);
|
||||
if (in == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
||||
|
||||
|
||||
int lastIndex = resourcePath.lastIndexOf('/');
|
||||
File outDir = new File(file, resourcePath.substring(0, Math.max(lastIndex, 0)));
|
||||
|
||||
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
||||
if (!file.exists() || replace) {
|
||||
try {
|
||||
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InputStream getResource(@NotNull String filename) {
|
||||
try {
|
||||
URL url = this.getClass().getClassLoader().getResource(filename);
|
||||
if (url == null) return null;
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
return connection.getInputStream();
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class MapFactory<S extends Map<K, V>, K, V> {
|
||||
|
||||
private final S map;
|
||||
|
||||
protected MapFactory(S map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> put(K key, V value) {
|
||||
this.map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> remove(K key) {
|
||||
this.map.remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> clear() {
|
||||
this.map.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public S build() {
|
||||
return get();
|
||||
}
|
||||
|
||||
public S get() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap() {
|
||||
return new MapFactory<>(new HashMap<>());
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>hashMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap() {
|
||||
return of(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>linkedMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap() {
|
||||
return of(new TreeMap<>());
|
||||
}
|
||||
|
||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>treeMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <M extends Map<K, V>, K, V> MapFactory<M, K, V> of(M map) {
|
||||
return new MapFactory<>(map);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
|
||||
|
||||
|
||||
protected @Nullable T cachedValue;
|
||||
protected long parsedTime = -1;
|
||||
|
||||
public CachedConfigValue(@Nullable ConfigurationProvider provider, @Nullable String sectionPath,
|
||||
@NotNull String[] comments, @Nullable T defaultValue) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
}
|
||||
|
||||
protected T updateCache(T value) {
|
||||
this.parsedTime = System.currentTimeMillis();
|
||||
this.cachedValue = value;
|
||||
return getCachedValue();
|
||||
}
|
||||
|
||||
public @Nullable T getCachedValue() {
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this.parsedTime <= 0 || getProvider().isExpired(this.parsedTime);
|
||||
}
|
||||
|
||||
protected final T useDefault() {
|
||||
return useOrDefault(null);
|
||||
}
|
||||
|
||||
protected final T useOrDefault(@Nullable T value) {
|
||||
return updateCache(this.defaultValue == null ? value : this.defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class ConfigValue<T> {
|
||||
|
||||
public static @NotNull ConfigBuilder builder() {
|
||||
return new ConfigBuilder();
|
||||
}
|
||||
|
||||
protected @Nullable T defaultValue;
|
||||
|
||||
protected @Nullable ConfigurationProvider provider;
|
||||
protected @Nullable String configPath;
|
||||
protected @NotNull String[] comments;
|
||||
|
||||
public ConfigValue(@Nullable ConfigurationProvider provider, @Nullable String configPath,
|
||||
@NotNull String[] comments, @Nullable T defaultValue) {
|
||||
this.provider = provider;
|
||||
this.configPath = configPath;
|
||||
this.comments = comments;
|
||||
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationProvider provider, @NotNull String configPath,
|
||||
@NotNull String... comments) {
|
||||
if (this.provider == null) this.provider = provider;
|
||||
if (this.configPath == null) this.configPath = configPath;
|
||||
if (this.comments.length == 0) this.comments = comments;
|
||||
|
||||
this.provider.setComments(this.configPath, this.comments);
|
||||
get();
|
||||
}
|
||||
|
||||
public @Nullable T getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(@Nullable T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public abstract @Nullable T get();
|
||||
|
||||
public @Nullable T getOrDefault() {
|
||||
return Optional.ofNullable(get()).orElse(getDefaultValue());
|
||||
}
|
||||
|
||||
public @NotNull T getNotNull() {
|
||||
return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null.");
|
||||
}
|
||||
|
||||
public @NotNull Optional<@Nullable T> getOptional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
public abstract void set(@Nullable T value);
|
||||
|
||||
public void setDefault() {
|
||||
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationProvider getProvider() {
|
||||
return Optional.ofNullable(this.provider)
|
||||
.orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider."));
|
||||
}
|
||||
|
||||
public final @NotNull ConfigurationWrapper getConfiguration() {
|
||||
try {
|
||||
return getProvider().getConfiguration();
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("Value(" + configPath + ") has not been initialized", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull String getConfigPath() {
|
||||
return Optional.ofNullable(this.configPath)
|
||||
.orElseThrow(() -> new IllegalStateException("No section path provided."));
|
||||
}
|
||||
|
||||
protected Object getValue() {
|
||||
return getConfiguration().get(getConfigPath());
|
||||
}
|
||||
|
||||
protected void setValue(@Nullable Object value) {
|
||||
getConfiguration().set(getConfigPath(), value);
|
||||
}
|
||||
|
||||
public String[] getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(String[] comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
|
||||
|
||||
public static <V> @NotNull ConfigListBuilder<V> builder(@NotNull Class<V> valueClass) {
|
||||
return builder().asList(valueClass);
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<Object, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
||||
|
||||
public ConfiguredList(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable List<V> defaultValue,
|
||||
@NotNull ConfigDataFunction<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<V> get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
List<V> list = new ArrayList<>();
|
||||
|
||||
List<?> data = getConfiguration().getList(getConfigPath());
|
||||
if (data == null || data.isEmpty()) return useOrDefault(list);
|
||||
|
||||
for (Object dataVal : data) {
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
list.add(parser.parse(dataVal));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(list);
|
||||
} else if (getCachedValue() != null) return getCachedValue();
|
||||
else if (getDefaultValue() != null) return getDefaultValue();
|
||||
else return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable List<V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
List<Object> data = new ArrayList<>();
|
||||
for (V val : value) {
|
||||
if (val == null) continue;
|
||||
try {
|
||||
data.add(serializer.parse(val));
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
|
||||
|
||||
public static <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> builder(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return builder().asMap(keyClass, valueClass);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<String, K> keyParser;
|
||||
protected final @NotNull ConfigDataFunction<Object, V> valueParser;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
|
||||
|
||||
public ConfiguredMap(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@Nullable Map<K, V> defaultValue, @NotNull Supplier<? extends Map<K, V>> supplier,
|
||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
|
||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
||||
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
this.keyParser = keyParser;
|
||||
this.valueParser = valueParser;
|
||||
this.keySerializer = keySerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<K> getKeyClass() {
|
||||
return keyClass;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
|
||||
return keyParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<Object, V> getValueParser() {
|
||||
return valueParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<K, V> get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
Map<K, V> map = supplier.get();
|
||||
|
||||
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return useOrDefault(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
if (keys.isEmpty()) return useOrDefault(map);
|
||||
|
||||
for (String dataKey : keys) {
|
||||
Object dataVal = section.get(dataKey);
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
K key = keyParser.parse(dataKey);
|
||||
V value = valueParser.parse(dataVal);
|
||||
map.put(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(map);
|
||||
} else if (getCachedValue() != null) return getCachedValue();
|
||||
else if (getDefaultValue() != null) return getDefaultValue();
|
||||
else return supplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Map<K, V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : value.entrySet()) {
|
||||
try {
|
||||
String key = keySerializer.parse(entry.getKey());
|
||||
Object val = valueSerializer.parse(entry.getValue());
|
||||
data.put(key, val);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.value.SectionValueBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfiguredSection<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> @NotNull SectionValueBuilder<V> builder(@NotNull Class<V> valueClass) {
|
||||
return builder().asValue(valueClass).fromSection();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public ConfiguredSection(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable V defaultValue,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigValueParser<ConfigurationWrapper, V> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable V get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return useDefault();
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(this.parser.parse(section, this.defaultValue));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return useDefault();
|
||||
}
|
||||
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
try {
|
||||
setValue(serializer.parse(value));
|
||||
// getConfiguration().createSection(getSectionPath(), serializer.parse(value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> ConfigValueBuilder<V> builder(Class<V> valueClass) {
|
||||
return builder().asValue(valueClass);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass) {
|
||||
return of(valueClass, null);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass, @Nullable V defaultValue) {
|
||||
return builder(valueClass).fromObject().defaults(defaultValue).build();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigValueParser<Object, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
||||
|
||||
public ConfiguredValue(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable V defaultValue,
|
||||
@NotNull ConfigValueParser<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigValueParser<Object, V> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
Object value = getConfiguration().get(getConfigPath());
|
||||
if (value == null) return useDefault(); // 获取的值不存在,直接使用默认值。
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(this.parser.parse(value, this.defaultValue));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return useDefault();
|
||||
}
|
||||
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
try {
|
||||
setValue(serializer.parse(value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigExceptionHandler {
|
||||
|
||||
void handle(@NotNull String path, @NotNull Throwable throwable);
|
||||
|
||||
static @NotNull ConfigExceptionHandler silence() {
|
||||
return (path, throwable) -> {
|
||||
};
|
||||
}
|
||||
|
||||
static @NotNull ConfigExceptionHandler print() {
|
||||
return (path, throwable) -> {
|
||||
System.err.println("Error occurred at path: " + path);
|
||||
throwable.printStackTrace();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DataConsumer<T> extends Serializable {
|
||||
|
||||
void accept(@NotNull T data) throws Exception;
|
||||
|
||||
default DataConsumer<T> andThen(DataConsumer<? super T> after) {
|
||||
return (T t) -> {
|
||||
accept(t);
|
||||
after.accept(t);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+47
-53
@@ -1,87 +1,80 @@
|
||||
package cc.carm.lib.configuration.core.function;
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigValueParser<T, R> {
|
||||
public interface DataFunction<T, R> extends Serializable {
|
||||
|
||||
@Nullable R parse(@NotNull T data, @Nullable R defaultValue) throws Exception;
|
||||
@NotNull R handle(@NotNull T data) throws Exception;
|
||||
|
||||
default <V> ConfigValueParser<T, V> andThen(@NotNull ConfigValueParser<R, V> after) {
|
||||
default <V> @NotNull DataFunction<T, V> andThen(@NotNull DataFunction<? super R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((data, defaultValue) -> {
|
||||
R result = parse(data, null);
|
||||
if (result == null) return defaultValue;
|
||||
else return after.parse(result, defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ConfigValueParser<V, R> compose(@NotNull ConfigDataFunction<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((data, defaultValue) -> {
|
||||
T result = before.parse(data);
|
||||
return parse(result, defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigValueParser<T, T> identity() {
|
||||
return (input, defaultValue) -> input;
|
||||
return data -> after.handle(handle(data));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigValueParser<T, Object> toObject() {
|
||||
return (input, defaultValue) -> input;
|
||||
static <T> @NotNull DataFunction<T, T> identity() {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ConfigValueParser<T, V> required() {
|
||||
return (input, defaultValue) -> {
|
||||
static <T> @NotNull DataFunction<T, T> identity(Class<T> type) {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull DataFunction<T, V> required() {
|
||||
return input -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
|
||||
return (input, defaultValue) -> {
|
||||
static <T> @NotNull DataFunction<T, Object> toObject() {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull DataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigValueParser<String, V> parseString(Class<V> valueClass) {
|
||||
return (input, defaultValue) -> {
|
||||
static <V> @NotNull DataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, String> castToString() {
|
||||
return (input, defaultValue) -> {
|
||||
static <T> @NotNull DataFunction<T, String> castToString() {
|
||||
return input -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
||||
else return input.toString();
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull <T> ConfigValueParser<T, String> castToString(Class<T> clazz) {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else return input.toString();
|
||||
static <V> @NotNull DataFunction<String, V> parseString(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Integer> intValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Integer> intValue() {
|
||||
return input -> {
|
||||
if (input instanceof Integer) {
|
||||
return (Integer) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -91,8 +84,8 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Short> shortValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Short> shortValue() {
|
||||
return input -> {
|
||||
if (input instanceof Short) {
|
||||
return (Short) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -102,8 +95,8 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Double> doubleValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Double> doubleValue() {
|
||||
return input -> {
|
||||
if (input instanceof Double) {
|
||||
return (Double) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -113,8 +106,8 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Byte> byteValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Byte> byteValue() {
|
||||
return input -> {
|
||||
if (input instanceof Byte) {
|
||||
return (Byte) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -124,8 +117,8 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Float> floatValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Float> floatValue() {
|
||||
return input -> {
|
||||
if (input instanceof Float) {
|
||||
return (Float) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -135,8 +128,8 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Long> longValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Long> longValue() {
|
||||
return input -> {
|
||||
if (input instanceof Long) {
|
||||
return (Long) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -146,19 +139,20 @@ public interface ConfigValueParser<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Boolean> booleanValue() {
|
||||
return (input, defaultValue) -> {
|
||||
static @NotNull DataFunction<Object, Boolean> booleanValue() {
|
||||
return input -> {
|
||||
if (input instanceof Boolean) {
|
||||
return (Boolean) input;
|
||||
} else if (input instanceof String) {
|
||||
String s = (String) input;
|
||||
return Boolean.parseBoolean(s) || "yes".equalsIgnoreCase(s);
|
||||
} else if (input instanceof Integer) {
|
||||
return ((Integer) input) == 1;
|
||||
return (Integer) input == 1;
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Boolean.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DataValidator<T> extends Serializable {
|
||||
|
||||
void validate(@Nullable T value) throws Exception;
|
||||
|
||||
default DataValidator<T> compose(DataValidator<? super T> other) {
|
||||
return value -> {
|
||||
validate(value);
|
||||
other.validate(value);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ValueComposer<T, U> extends Serializable {
|
||||
|
||||
/**
|
||||
* Accept the value and the data, and then compose the value.
|
||||
*
|
||||
* @param holder The configuration holder
|
||||
* @param type The value type, e.g. {@link java.util.List}, {@link java.util.Map}, etc.
|
||||
* @param data The unit data
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull T type, @NotNull U data) throws Exception;
|
||||
|
||||
default ValueComposer<T, U> andThen(ValueComposer<? super T, ? super U> after) {
|
||||
return (holder, unit, data) -> {
|
||||
accept(holder, unit, data);
|
||||
after.accept(holder, unit, data);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ValueHandler<T, R> extends Serializable {
|
||||
|
||||
@Nullable R handle(@NotNull ConfigurationHolder<?> holder, @NotNull T data) throws Exception;
|
||||
|
||||
default <V> ValueHandler<T, V> andThen(@NotNull ValueHandler<R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((provider, data) -> {
|
||||
R result = handle(provider, data);
|
||||
if (result == null) return null;
|
||||
else return after.handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ValueHandler<V, R> compose(@NotNull ValueHandler<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((provider, data) -> {
|
||||
T result = before.handle(provider, data);
|
||||
if (result == null) return null;
|
||||
return handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ValueHandler<V, R> compose(@NotNull DataFunction<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((provider, data) -> {
|
||||
T result = before.handle(data);
|
||||
return handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, T> identity() {
|
||||
return (provider, input) -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, Object> toObject() {
|
||||
return ConfigurationHolder::serialize;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, String> stringValue() {
|
||||
return (provider, input) -> String.valueOf(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <O, T> @NotNull ValueHandler<O, T> deserialize(ValueType<T> to) {
|
||||
return (provider, input) -> provider.deserialize(to, input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ValueHandler<T, V> required() {
|
||||
return (provider, input) -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ValueHandler<T, V> required(ValueType<V> type) {
|
||||
return (provider, input) -> {
|
||||
if (type.isInstance(input)) return type.cast(input); // Simple cast
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ValueValidator<T> extends Serializable {
|
||||
|
||||
void validate(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception;
|
||||
|
||||
default ValueValidator<T> and(ValueValidator<? super T> other) {
|
||||
return (holder, value) -> {
|
||||
validate(holder, value);
|
||||
other.validate(holder, value);
|
||||
};
|
||||
}
|
||||
|
||||
static <V> ValueValidator<V> none() {
|
||||
return (holder, data) -> {
|
||||
};
|
||||
}
|
||||
|
||||
static <V> ValueValidator<V> nonnull() {
|
||||
return nonnull("Value cannot be null");
|
||||
}
|
||||
|
||||
static <V> ValueValidator<V> nonnull(String message) {
|
||||
return (holder, data) -> {
|
||||
if (data == null) throw new IllegalArgumentException(message);
|
||||
};
|
||||
}
|
||||
|
||||
static <V extends Number> ValueValidator<V> range(V min, V max) {
|
||||
return range(min, max, "Value must be in range [" + min + ", " + max + "]");
|
||||
}
|
||||
|
||||
static <V extends Number> ValueValidator<V> range(V min, V max, String message) {
|
||||
return (holder, data) -> {
|
||||
if (data.doubleValue() < min.doubleValue() || data.doubleValue() > max.doubleValue()) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.*;
|
||||
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters;
|
||||
import cc.carm.lib.configuration.function.ConfigExceptionHandler;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueValidator;
|
||||
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* ConfigurationFactory, used to create configuration holder.
|
||||
*
|
||||
* @param <SOURCE> The {@link ConfigureSource} type
|
||||
* @param <HOLDER> The {@link ConfigurationHolder} type.
|
||||
* @param <SELF> Self builder, for further implement support.
|
||||
*/
|
||||
public abstract class ConfigurationFactory<
|
||||
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
||||
HOLDER extends ConfigurationHolder<SOURCE>,
|
||||
SELF
|
||||
> {
|
||||
|
||||
protected @NotNull ValueAdapterRegistry adapters = new ValueAdapterRegistry();
|
||||
protected @NotNull ConfigurationOptionHolder options = new ConfigurationOptionHolder();
|
||||
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
|
||||
protected @NotNull ConfigurationInitializer initializer = new ConfigurationInitializer();
|
||||
protected @NotNull ConfigExceptionHandler exceptionHandler = ConfigExceptionHandler.print();
|
||||
|
||||
protected ConfigurationFactory() {
|
||||
this.adapters.register(StandardAdapters.PRIMITIVES);
|
||||
this.adapters.register(StandardAdapters.SECTIONS);
|
||||
this.adapters.register(StandardAdapters.ENUMS);
|
||||
this.adapters.register(StandardAdapters.UUID);
|
||||
}
|
||||
|
||||
protected abstract SELF self();
|
||||
|
||||
public SELF adapters(ValueAdapterRegistry adapters) {
|
||||
this.adapters = adapters;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF adapter(Consumer<ValueAdapterRegistry> adapterRegistryConsumer) {
|
||||
adapterRegistryConsumer.accept(adapters);
|
||||
return self();
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueAdapter<T> adapter) {
|
||||
return adapter(a -> a.register(adapter));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
return adapter(a -> a.register(type, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, parser));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, serializer, parser));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(ValueType.of(type), serializer, parser);
|
||||
}
|
||||
|
||||
public SELF options(ConfigurationOptionHolder options) {
|
||||
this.options = options;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF option(Consumer<ConfigurationOptionHolder> modifier) {
|
||||
modifier.accept(options);
|
||||
return self();
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, O value) {
|
||||
return option(o -> o.set(type, value));
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, Supplier<O> value) {
|
||||
return option(type, value.get());
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, Consumer<O> modifier) {
|
||||
return option(holder -> {
|
||||
O current = holder.get(type);
|
||||
modifier.accept(current);
|
||||
holder.set(type, current);
|
||||
});
|
||||
}
|
||||
|
||||
public SELF metadata(@NotNull Map<String, ConfigurationMetaHolder> metadata) {
|
||||
this.metadata = metadata;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF metadata(@NotNull Consumer<Map<String, ConfigurationMetaHolder>> handler) {
|
||||
handler.accept(this.metadata);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF metadata(@Nullable String path, @NotNull ConfigurationMetaHolder meta) {
|
||||
return metadata(m -> m.put(path, meta));
|
||||
}
|
||||
|
||||
public SELF metadata(@Nullable String path, @NotNull Consumer<ConfigurationMetaHolder> handler) {
|
||||
return metadata(map -> {
|
||||
ConfigurationMetaHolder meta = map.computeIfAbsent(path, k -> new ConfigurationMetaHolder());
|
||||
handler.accept(meta);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public SELF initializer(ConfigurationInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF initializer(Consumer<ConfigurationInitializer> initializerConsumer) {
|
||||
initializerConsumer.accept(initializer);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF exceptionally(@NotNull ConfigExceptionHandler handler) {
|
||||
this.exceptionHandler = handler;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply the base path generator for this configuration holder
|
||||
*
|
||||
* @param generator {@link PathGenerator}
|
||||
* @return this
|
||||
*/
|
||||
public SELF pathGenerator(PathGenerator generator) {
|
||||
return initializer(loader -> loader.pathGenerator(generator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new annotation for metadata to the configuration loader
|
||||
*
|
||||
* @param annotation The {@link Annotation}
|
||||
* @param metadata The {@link ConfigurationMetadata} type
|
||||
* @param extractor The {@link Function} to extract the metadata from the annotation
|
||||
* @param <M> The metadata type
|
||||
* @param <A> The annotation type
|
||||
* @return this
|
||||
*/
|
||||
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<M> metadata,
|
||||
@NotNull Function<A, M> extractor) {
|
||||
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new annotation for {@link ValueValidator} to the configuration loader
|
||||
*
|
||||
* @param annotation The {@link Annotation}
|
||||
* @param builder The {@link Function} to build the {@link ValueValidator} from the annotation
|
||||
* @param <A> The annotation type
|
||||
* @return this
|
||||
*/
|
||||
public <A extends Annotation> SELF validAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull Function<A, ValueValidator<Object>> builder) {
|
||||
return initializer(loader -> loader.registerValidAnnotation(annotation, builder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the configuration holder.
|
||||
*
|
||||
* @return The configuration holder
|
||||
*/
|
||||
public abstract @NotNull HOLDER build();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ConfigExceptionHandler;
|
||||
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.source.meta.StandardMeta;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
||||
|
||||
protected final @NotNull ValueAdapterRegistry adapters;
|
||||
protected final @NotNull ConfigurationOptionHolder options;
|
||||
protected final @NotNull Map<String, ConfigurationMetaHolder> metadata;
|
||||
|
||||
protected final @NotNull ConfigurationInitializer initializer;
|
||||
|
||||
protected @NotNull ConfigExceptionHandler exceptionHandler;
|
||||
|
||||
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||
@NotNull ConfigurationOptionHolder options,
|
||||
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||
@NotNull ConfigurationInitializer initializer) {
|
||||
this(adapters, options, metadata, initializer, ConfigExceptionHandler.print());
|
||||
}
|
||||
|
||||
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||
@NotNull ConfigurationOptionHolder options,
|
||||
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||
@NotNull ConfigurationInitializer initializer,
|
||||
@NotNull ConfigExceptionHandler exceptionHandler) {
|
||||
this.initializer = initializer;
|
||||
this.adapters = adapters;
|
||||
this.options = options;
|
||||
this.metadata = metadata;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
public abstract @NotNull SOURCE config();
|
||||
|
||||
public void reload() throws Exception {
|
||||
config().reload();
|
||||
}
|
||||
|
||||
public void save() throws Exception {
|
||||
config().save();
|
||||
}
|
||||
|
||||
public ConfigurationOptionHolder options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public <O> @NotNull O option(@NotNull ConfigurationOption<O> option) {
|
||||
return options().get(option);
|
||||
}
|
||||
|
||||
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationMetaHolder metadata(@Nullable String path) {
|
||||
return metadata().computeIfAbsent(path, k -> new ConfigurationMetaHolder());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type) {
|
||||
return extractMetadata(type, Objects::nonNull);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<@Nullable M> filter) {
|
||||
Map<String, M> metas = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
|
||||
M data = entry.getValue().get(type);
|
||||
if (filter.test(data)) metas.put(entry.getKey(), data);
|
||||
}
|
||||
return Collections.unmodifiableMap(metas);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
public Map<String, ConfigValue<?, ?>> registeredValues() {
|
||||
return extractMetadata(StandardMeta.VALUE);
|
||||
}
|
||||
|
||||
public ValueAdapterRegistry adapters() {
|
||||
return this.adapters;
|
||||
}
|
||||
|
||||
public ConfigurationInitializer initializer() {
|
||||
return initializer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
return adapters().deserialize(this, type, source);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||
return adapters().deserialize(this, type, source);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> Object serialize(@Nullable T value) throws Exception {
|
||||
return adapters().serialize(this, value);
|
||||
}
|
||||
|
||||
public void initialize(Class<? extends Configuration> configClass) {
|
||||
try {
|
||||
initializer.initialize(this, configClass);
|
||||
} catch (Exception e) {
|
||||
throwing(configClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize(@NotNull Configuration config) {
|
||||
try {
|
||||
initializer.initialize(this, config);
|
||||
} catch (Exception e) {
|
||||
throwing(config.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ValueManifest<?, ?> value) {
|
||||
value.holder(this);
|
||||
}
|
||||
|
||||
public void throwing(@NotNull String path, @NotNull Throwable e) {
|
||||
this.exceptionHandler.handle(path, e);
|
||||
}
|
||||
|
||||
public void exceptionally(@NotNull ConfigExceptionHandler handler) {
|
||||
this.exceptionHandler = handler;
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigInitializeHandler<T, V> {
|
||||
|
||||
static <T, V> ConfigInitializeHandler<T, V> start() {
|
||||
return (provider, path, value, instance) -> {
|
||||
};
|
||||
}
|
||||
|
||||
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path,
|
||||
@NotNull T value, @Nullable V instance) throws Exception;
|
||||
|
||||
default ConfigInitializeHandler<T, V> andThen(ConfigInitializeHandler<T, V> after) {
|
||||
return (provider, path, value, instance) -> {
|
||||
whenInitialize(provider, path, value, instance);
|
||||
after.whenInitialize(provider, path, value, instance);
|
||||
};
|
||||
}
|
||||
|
||||
default ConfigInitializeHandler<T, V> compose(ConfigInitializeHandler<T, V> before) {
|
||||
return (provider, path, value, instance) -> {
|
||||
before.whenInitialize(provider, path, value, instance);
|
||||
whenInitialize(provider, path, value, instance);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.function.ValueValidator;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.source.meta.StandardMeta;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Configuration initializer,
|
||||
* used to initialize {@link ConfigValue}s from {@link Configuration} classes.
|
||||
*/
|
||||
public class ConfigurationInitializer {
|
||||
|
||||
protected @NotNull PathGenerator pathGenerator;
|
||||
protected @NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer;
|
||||
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer;
|
||||
|
||||
public ConfigurationInitializer() {
|
||||
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
||||
}
|
||||
|
||||
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
||||
@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer,
|
||||
@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||
this.pathGenerator = pathGenerator;
|
||||
this.valueInitializer = valueInitializer;
|
||||
this.classInitializer = classInitializer;
|
||||
}
|
||||
|
||||
public void pathGenerator(@NotNull PathGenerator pathGenerator) {
|
||||
this.pathGenerator = pathGenerator;
|
||||
}
|
||||
|
||||
public @NotNull PathGenerator pathGenerator() {
|
||||
return pathGenerator;
|
||||
}
|
||||
|
||||
public ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer() {
|
||||
return valueInitializer;
|
||||
}
|
||||
|
||||
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
|
||||
this.valueInitializer = fieldInitializer;
|
||||
}
|
||||
|
||||
public ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer() {
|
||||
return classInitializer;
|
||||
}
|
||||
|
||||
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||
this.classInitializer = classInitializer;
|
||||
}
|
||||
|
||||
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
|
||||
this.valueInitializer = this.valueInitializer.andThen(fieldInitializer);
|
||||
}
|
||||
|
||||
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||
this.classInitializer = this.classInitializer.andThen(classInitializer);
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerClassAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
appendClassInitializer((holder, path, clazz, instance) -> {
|
||||
A data = clazz.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerFieldAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
appendFieldInitializer((holder, path, field, instance) -> {
|
||||
A data = field.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
registerClassAnnotation(annotation, metadata, extractor);
|
||||
registerFieldAnnotation(annotation, metadata, extractor);
|
||||
}
|
||||
|
||||
public <A extends Annotation> void registerValidAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull Function<A, ValueValidator<Object>> builder) {
|
||||
appendFieldInitializer((holder, path, field, instance) -> {
|
||||
A data = field.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
instance.validate((h, t) -> builder.apply(data).validate(h, t));
|
||||
});
|
||||
}
|
||||
|
||||
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
|
||||
return pathGenerator.getFieldPath(holder, parentPath, field);
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath,
|
||||
@NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||
return pathGenerator.getClassPath(holder, parentPath, clazz, clazzField);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Configuration config) throws Exception {
|
||||
initializeInstance(holder, config, null, null);
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Class<? extends Configuration> clazz) throws Exception {
|
||||
initializeStaticClass(holder, clazz, null, null);
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||
}
|
||||
|
||||
|
||||
// 针对实例类的初始化方法
|
||||
protected void initializeInstance(@NotNull ConfigurationHolder<?> holder, @NotNull Configuration root,
|
||||
@Nullable String parentPath, @Nullable Field configField) {
|
||||
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
||||
try {
|
||||
this.classInitializer.whenInitialize(holder, path, root.getClass(), root);
|
||||
} catch (Exception e) {
|
||||
holder.throwing(path, e);
|
||||
}
|
||||
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
|
||||
}
|
||||
|
||||
// 针对静态类的初始化方法
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void initializeStaticClass(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Class<?> clazz,
|
||||
@Nullable String parentPath, @Nullable Field configField) {
|
||||
if (!Configuration.class.isAssignableFrom(clazz)) return; // Only Configuration class can be initialized.
|
||||
|
||||
String path = getClassPath(holder, parentPath, clazz, configField);
|
||||
|
||||
try {
|
||||
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField);
|
||||
} catch (Exception e) {
|
||||
holder.throwing(path, e);
|
||||
}
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
initializeField(holder, clazz, field, path);
|
||||
}
|
||||
|
||||
if (holder.option(StandardOptions.LOAD_SUB_CLASSES)) {
|
||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||
initializeStaticClass(holder, classes[i], path, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeField(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Object source, @NotNull Field field, @Nullable String parent) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Object object = field.get(source);
|
||||
//
|
||||
if (object instanceof ConfigValue<?, ?>) {
|
||||
// 目标是 ConfigValue 实例,进行具体的初始化注入
|
||||
ConfigValue<?, ?> value = (ConfigValue<?, ?>) object;
|
||||
String path = getFieldPath(holder, parent, field);
|
||||
if (path == null) return;
|
||||
value.initialize(holder, path);
|
||||
holder.metadata(path).set(StandardMeta.VALUE, value); // Mark the minimal config value unit.
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) {
|
||||
value.setDefault(); // Set default value.
|
||||
}
|
||||
try {
|
||||
this.valueInitializer.whenInitialize(holder, path, field, value);
|
||||
} catch (Exception e) {
|
||||
holder.throwing(path, e);
|
||||
}
|
||||
if (holder.option(StandardOptions.PRELOAD)) {
|
||||
value.get(); // Preload the value by calling #get method.
|
||||
}
|
||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
||||
initializeInstance(holder, (Configuration) object, parent, field);
|
||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||
initializeStaticClass(holder, (Class<?>) object, parent, field);
|
||||
}
|
||||
|
||||
// 以上判断实现以下规范:
|
||||
// - 实例类中仅加载 ConfigValue实例 与 Configuration实例
|
||||
// - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类
|
||||
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Path generator for configuration.
|
||||
* <p>
|
||||
* Path generator is a utility class that helps to generate the path of the configuration.
|
||||
* It can be used to generate the path of the field or class.
|
||||
*/
|
||||
public class PathGenerator {
|
||||
|
||||
public static PathGenerator of() {
|
||||
return of(PathGenerator::covertPathName);
|
||||
}
|
||||
|
||||
public static PathGenerator of(UnaryOperator<String> pathConverter) {
|
||||
return new PathGenerator(pathConverter);
|
||||
}
|
||||
|
||||
protected UnaryOperator<String> pathConverter;
|
||||
|
||||
public PathGenerator(UnaryOperator<String> pathConverter) {
|
||||
this.pathConverter = pathConverter;
|
||||
}
|
||||
|
||||
public @NotNull UnaryOperator<String> getPathConverter() {
|
||||
return pathConverter;
|
||||
}
|
||||
|
||||
public void setPathConverter(UnaryOperator<String> pathConverter) {
|
||||
this.pathConverter = pathConverter;
|
||||
}
|
||||
|
||||
public String covertPath(String name) {
|
||||
return pathConverter.apply(name);
|
||||
}
|
||||
|
||||
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parentPath, @NotNull Field field) {
|
||||
ConfigPath path = field.getAnnotation(ConfigPath.class);
|
||||
if (path == null)
|
||||
return link(holder, parentPath, false, covertPath(field.getName())); // No annotation, use field name.
|
||||
else return link(holder, parentPath, path.root(), select(path.value(), covertPath(field.getName())));
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parentPath, @NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||
// For standard path generator, we generate path following by:
|
||||
// 1. Check if the class has a ConfigPath annotation, if so, use the root and value as the path.
|
||||
// 2. If the class defined as a field, check if the field has a ConfigPath annotation,
|
||||
// and use filed information.
|
||||
ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class);
|
||||
if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value());
|
||||
|
||||
if (clazzField != null) {
|
||||
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
|
||||
if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName()));
|
||||
else return getFieldPath(holder, parentPath, clazzField);
|
||||
}
|
||||
|
||||
return link(holder, parentPath, false, covertPath(clazz.getSimpleName())); // No field, use class name.
|
||||
|
||||
}
|
||||
|
||||
protected String select(String path, String defaultValue) {
|
||||
if (path == null || path.isEmpty()) return defaultValue;
|
||||
else return isBlank(path) ? null : path;
|
||||
}
|
||||
|
||||
protected @Nullable String link(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parent, boolean root, @Nullable String path) {
|
||||
if (path == null || path.isEmpty()) return root ? null : parent;
|
||||
return (root || parent == null) ? path : (parent + pathSeparator(holder) + path);
|
||||
}
|
||||
|
||||
public static boolean isBlank(String path) {
|
||||
return path == null || path.replace(" ", "").isEmpty();
|
||||
}
|
||||
|
||||
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
||||
return holder.option(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration name of the specified element.
|
||||
* Use the naming convention of all lowercase and "-" links.
|
||||
* <p>
|
||||
* e.g. "SOME_NAME" will be "some-name"
|
||||
*
|
||||
* @param name source name
|
||||
* @return the final path
|
||||
*/
|
||||
public static String covertPathName(String name) {
|
||||
return name
|
||||
// Replace all uppercase letters with dashes
|
||||
.replaceAll("[A-Z]", "=$0")
|
||||
// If the first letter is also capitalized,
|
||||
// it will also be converted and the first dash will need to be removed
|
||||
.replaceAll("^=(.*)$", "$1")
|
||||
// Because the name may contain _, it needs to be treated a little differently
|
||||
.replaceAll("_=([A-Z])", "_$1")
|
||||
// The content that is not named in all caps is then converted
|
||||
.replaceAll("([a-z])=([A-Z])", "$1_$2")
|
||||
// Remove any extra horizontal lines
|
||||
.replace("=", "")
|
||||
// Replace the underscore with a dash
|
||||
.replace("_", "-")
|
||||
// Finally, convert it to all lowercase
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationMetaHolder {
|
||||
|
||||
protected final @NotNull Map<ConfigurationMetadata<?>, Object> values;
|
||||
|
||||
public ConfigurationMetaHolder() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
public ConfigurationMetaHolder(@NotNull Map<ConfigurationMetadata<?>, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public @NotNull Map<ConfigurationMetadata<?>, Object> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param defaultValue Default value if the value of option is not set.
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Contract("_,!null -> !null")
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, @Nullable V defaultValue) {
|
||||
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param defaultValue Default value if the value of option is not set.
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Contract("_,!null -> !null")
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, Supplier<@Nullable V> defaultValue) {
|
||||
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type) {
|
||||
return get(type, (V) null);
|
||||
}
|
||||
|
||||
public boolean contains(@NotNull ConfigurationMetadata<?> type) {
|
||||
return values().containsKey(type) || type.hasDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will be removed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
* @return Previous value of meta
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V set(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
if (value == null || type.isDefault(value)) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
*/
|
||||
public <V> void setIfAbsent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
if (value == null || type.isDefault(value)) {
|
||||
values().remove(type);
|
||||
} else {
|
||||
values().putIfAbsent(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
* @return Previous value of meta
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V setIfPresent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
Object exists = values().get(type);
|
||||
if (exists == null) return null;
|
||||
|
||||
if (value == null || type.isDefault(value)) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationMetadata<T> {
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of() {
|
||||
return of(() -> null);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of(T defaults) {
|
||||
return of(() -> defaults);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of(@NotNull Supplier<@Nullable T> defaults) {
|
||||
return new ConfigurationMetadata<>(defaults);
|
||||
}
|
||||
|
||||
protected Supplier<@Nullable T> defaultsSupplier;
|
||||
|
||||
public ConfigurationMetadata(@NotNull Supplier<@Nullable T> defaults) {
|
||||
this.defaultsSupplier = defaults;
|
||||
}
|
||||
|
||||
public boolean isDefault(@NotNull T value) {
|
||||
return value.equals(defaults());
|
||||
}
|
||||
|
||||
public boolean hasDefaults() {
|
||||
return defaults() != null;
|
||||
}
|
||||
|
||||
public T defaultOrSupply(@Nullable T suppliedValue) {
|
||||
return defaultOrSupply(() -> suppliedValue);
|
||||
}
|
||||
|
||||
public T defaultOrSupply(Supplier<@Nullable T> suppliedValue) {
|
||||
T defaults = defaults();
|
||||
return defaults == null ? suppliedValue.get() : defaults;
|
||||
}
|
||||
|
||||
public @Nullable T defaults() {
|
||||
return defaultsSupplier.get();
|
||||
}
|
||||
|
||||
public void setDefaults(Supplier<T> defaultFunction) {
|
||||
this.defaultsSupplier = defaultFunction;
|
||||
}
|
||||
|
||||
public void setDefaults(T value) {
|
||||
setDefaults(() -> value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
|
||||
public interface StandardMeta {
|
||||
|
||||
/**
|
||||
* To mark the {@link ConfigValue} instance of specific path.
|
||||
*/
|
||||
ConfigurationMetadata<ConfigValue<?, ?>> VALUE = ConfigurationMetadata.of();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationOption<V> {
|
||||
|
||||
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
|
||||
return new ConfigurationOption<>(defaultValue);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationOption<T> of(@NotNull Supplier<T> defaultValue) {
|
||||
return of(defaultValue.get());
|
||||
}
|
||||
|
||||
private @NotNull V defaultValue;
|
||||
|
||||
public ConfigurationOption(@NotNull V defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public @NotNull V defaults() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value of option.
|
||||
*
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
public void defaults(@NotNull V defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public boolean isDefault(@NotNull V value) {
|
||||
return value.equals(defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ConfigurationOptionHolder {
|
||||
|
||||
public static @NotNull ConfigurationOptionHolder of(@NotNull Map<ConfigurationOption<?>, Object> options) {
|
||||
return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options));
|
||||
}
|
||||
|
||||
protected final Map<ConfigurationOption<?>, Object> options;
|
||||
|
||||
public ConfigurationOptionHolder() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
public ConfigurationOptionHolder(Map<ConfigurationOption<?>, Object> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public @NotNull Map<ConfigurationOption<?>, Object> values() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @NotNull V get(@NotNull ConfigurationOption<V> type) {
|
||||
return Optional.ofNullable(values().get(type)).map(v -> (V) v).orElseGet(type::defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param value Value of option
|
||||
* @param <V> Value type
|
||||
* @return Previous value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V set(@NotNull ConfigurationOption<V> type, @Nullable V value) {
|
||||
if (value == null) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of option to option's {@link ConfigurationOption#defaults()}.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param <V> Value type
|
||||
* @return Previous value of option
|
||||
*/
|
||||
public <V> @Nullable V clear(@NotNull ConfigurationOption<V> type) {
|
||||
return set(type, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
|
||||
import static cc.carm.lib.configuration.source.option.ConfigurationOption.of;
|
||||
|
||||
public interface StandardOptions {
|
||||
|
||||
/**
|
||||
* The configuration path separator.
|
||||
*/
|
||||
ConfigurationOption<Character> PATH_SEPARATOR = of('.');
|
||||
|
||||
/**
|
||||
* Whether to set and save default values if offered and not exists in configuration.
|
||||
*/
|
||||
ConfigurationOption<Boolean> SET_DEFAULTS = of(true);
|
||||
|
||||
/**
|
||||
* Whether to load subclasses of configuration class.
|
||||
*/
|
||||
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
|
||||
|
||||
/**
|
||||
* Whether to pre parse the config values,
|
||||
* may good to set true if you want to keep the config format.
|
||||
* <br> if true, the values will be parsed when
|
||||
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||
* <br> if false, the values will be parsed when calling
|
||||
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||
*/
|
||||
ConfigurationOption<Boolean> PRELOAD = of(true);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,920 @@
|
||||
package cc.carm.lib.configuration.source.section;
|
||||
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a section of a configuration.
|
||||
*
|
||||
* @author Carm
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface ConfigureSection {
|
||||
|
||||
/**
|
||||
* Gets the parent section of this section.
|
||||
* <p>
|
||||
* For root sections, this will return null.
|
||||
*
|
||||
* @return Parent section, or null if this is a root section.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
@Nullable ConfigureSection parent();
|
||||
|
||||
/**
|
||||
* Get the current section's path from {@link #parent()} of this section.
|
||||
*
|
||||
* @return The current path of this section, if {@link #isRoot()}, return empty string.
|
||||
*/
|
||||
@NotNull String path();
|
||||
|
||||
/**
|
||||
* Get the full path of this section.
|
||||
*
|
||||
* @return The full path of this section, if {@link #isRoot()}, return empty string.
|
||||
*/
|
||||
default @NotNull String fullPath() {
|
||||
if (parent() == null) return "";
|
||||
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path separator for the section.
|
||||
*
|
||||
* @return The path separator
|
||||
*/
|
||||
default char pathSeparator() {
|
||||
return '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this section is a root section.
|
||||
*
|
||||
* @return True if this section is a root section, false otherwise.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
default boolean isRoot() {
|
||||
return parent() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this section is empty.
|
||||
*
|
||||
* @return True if this section is empty, false otherwise.
|
||||
*/
|
||||
default boolean isEmpty() {
|
||||
return getValues(false).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of keys in this section.
|
||||
*
|
||||
* @return Number of keys in this section.
|
||||
*/
|
||||
default int size(boolean deep) {
|
||||
return getKeys(deep).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set containing all keys in this section.
|
||||
* <p>
|
||||
* If deep is set to true, then this will contain all the keys within any
|
||||
* child {@link ConfigureSection}s (and their children paths).
|
||||
* <p>
|
||||
* If deep is set to false, then this will contain only the keys of any
|
||||
* direct children, and not their own children.
|
||||
*
|
||||
* @param deep Whether to get a deep list.
|
||||
* @return Set of keys contained within this Section.
|
||||
*/
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
default Set<String> getKeys(boolean deep) {
|
||||
return getValues(deep).keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set containing all primary keys in this section.
|
||||
*
|
||||
* @return Set of keys contained within this Section.
|
||||
*/
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
default Set<String> keys() {
|
||||
return getKeys(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set containing all values in this section.
|
||||
* <p>
|
||||
* If deep is set to true, then this will contain all the keys within any
|
||||
* child {@link ConfigureSection}s (and their children paths).
|
||||
* <p>
|
||||
* If deep is set to false, then this will contain only the keys of any
|
||||
* direct children, and not their own children.
|
||||
*
|
||||
* @param deep Whether to get a deep list.
|
||||
* @return Map of data values contained within this Section.
|
||||
*/
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
Map<String, Object> getValues(boolean deep);
|
||||
|
||||
/**
|
||||
* Gets a set containing all key-values in this section.
|
||||
*
|
||||
* @return Map of data values contained within this Section.
|
||||
* @see #getValues(boolean)
|
||||
*/
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
default Map<String, Object> values() {
|
||||
return getValues(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this section as a map.
|
||||
* <p>
|
||||
* In this map, child {@link ConfigureSection}s will also be represented as {@link Map}s.
|
||||
*
|
||||
* @return Map of data values contained within this Section.
|
||||
*/
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
Map<String, Object> asMap();
|
||||
|
||||
/**
|
||||
* Create a stream of all values in this section.
|
||||
*
|
||||
* @return Stream of all values in this section.
|
||||
*/
|
||||
default Stream<Map.Entry<String, Object>> stream() {
|
||||
return values().entrySet().stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all key-values in this section (include child sections)
|
||||
*
|
||||
* @param action The action to apply to each key.
|
||||
*/
|
||||
default void forEach(@NotNull BiConsumer<String, Object> action) {
|
||||
values().forEach(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value at the given path.
|
||||
* <p>
|
||||
* Null values will be kept, if you want to remove a value use {@link #remove(String)}
|
||||
* Path separator depends on holder's
|
||||
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||
*
|
||||
* @param path The path to set the value at.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void set(@NotNull String path, @Nullable Object value);
|
||||
|
||||
/**
|
||||
* Removes the value at the given path.
|
||||
* <p>
|
||||
* Path separator depends on holder's
|
||||
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||
*
|
||||
* @param path The path to remove the value at.
|
||||
*/
|
||||
void remove(@NotNull String path);
|
||||
|
||||
/**
|
||||
* Check if the given path is present.
|
||||
* <p>
|
||||
* Path separator depends on holder's
|
||||
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present, false otherwise.
|
||||
*/
|
||||
default boolean contains(@NotNull String path) {
|
||||
return getKeys(true).contains(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value of given path is present.
|
||||
* <p>
|
||||
* Path separator depends on holder's
|
||||
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present, false otherwise.
|
||||
*/
|
||||
default boolean containsValue(@NotNull String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is specific type.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @param typeClass The type's class
|
||||
* @param <T> The type to check.
|
||||
* @return True if the value is present and is the correct type, false otherwise.
|
||||
*/
|
||||
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||
return typeClass.isInstance(get(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link List}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a list, false otherwise.
|
||||
*/
|
||||
default boolean isList(@NotNull String path) {
|
||||
return isType(path, List.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link List} from the specified path.
|
||||
*
|
||||
* @param path The path to get the {@link List}.
|
||||
* @return The list if the path exists and is a list, otherwise null.
|
||||
*/
|
||||
default @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link ConfigureSection}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a section, false otherwise.
|
||||
*/
|
||||
default boolean isSection(@NotNull String path) {
|
||||
return isType(path, ConfigureSection.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link ConfigureSection} from the specified path.
|
||||
*
|
||||
* @param path The path to get the section.
|
||||
* @return The section if the path exists and is a section, otherwise null.
|
||||
*/
|
||||
@Nullable
|
||||
default ConfigureSection getSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigureSection} with specified values.
|
||||
* <p> The {@link #parent()} of the new section will be this section.
|
||||
* <p> This section will not be saved until {@link #set(String, Object)} is called.
|
||||
* <p> If you want to create and use a section and set it to this section, use {@link #computeSection(String)}.
|
||||
*
|
||||
* @param data The data to be used to create section.
|
||||
* @return Newly created section
|
||||
*/
|
||||
@NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data);
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigureSection} with specified values.
|
||||
* <p> The {@link #parent()} of the new section will be this section.
|
||||
*
|
||||
* @param data The data to be used to create section.
|
||||
* @return Newly created section
|
||||
*/
|
||||
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||
return createSection(path, () -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
data.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigureSection} with specified values.
|
||||
* <p> The {@link #parent()} of the new section will be this section.
|
||||
*
|
||||
* @param data The data to be used to create section.
|
||||
* @return Newly created section
|
||||
*/
|
||||
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||
return createSection(path, data.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty {@link ConfigureSection}.
|
||||
* <p> The {@link #parent()} of the new section will be this section.
|
||||
*
|
||||
* @return Newly created section
|
||||
*/
|
||||
default @NotNull ConfigureSection createSection(@NotNull String path) {
|
||||
return createSection(path, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a section at the given path.
|
||||
* <p>
|
||||
* Any value previously set at this path will be replaced if it is not a section
|
||||
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||
*
|
||||
* @param path The path to get the section from.
|
||||
* @return The section at the path, or a new section if it does not exist.
|
||||
* @see #createSection(String, Map)
|
||||
*/
|
||||
default @NotNull ConfigureSection computeSection(@NotNull String path) {
|
||||
return computeSection(path, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a section at the given path.
|
||||
* <p>
|
||||
* Any value previously set at this path will be replaced if it is not a section
|
||||
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||
*
|
||||
* @param path The path to get the section from.
|
||||
* @return The section at the path, or a new section if it does not exist.
|
||||
* @see #createSection(String, Map)
|
||||
*/
|
||||
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||
ConfigureSection current = getSection(path);
|
||||
if (current == null) {
|
||||
current = createSection(path, data);
|
||||
set(path, current);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a section at the given path.
|
||||
* <p>
|
||||
* Any value previously set at this path will be replaced if it is not a section
|
||||
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||
*
|
||||
* @param path The path to get the section from.
|
||||
* @return The section at the path, or a new section if it does not exist.
|
||||
* @see #createSection(String, Map)
|
||||
*/
|
||||
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||
return computeSection(path, () -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
data.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a section at the given path.
|
||||
* <p>
|
||||
* Any value previously set at this path will be replaced if it is not a section
|
||||
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||
*
|
||||
* @param path The path to get the section from.
|
||||
* @return The section at the path, or a new section if it does not exist.
|
||||
* @see #createSection(String, Map)
|
||||
*/
|
||||
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||
return computeSection(path, data.get());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the origin value of the path.
|
||||
*
|
||||
* @param path The path to get the value from.
|
||||
* @return The value at the path, or null if not found.
|
||||
*/
|
||||
@Nullable Object get(@NotNull String path);
|
||||
|
||||
/**
|
||||
* Get the value of the path for specific type,
|
||||
* if the path does not exist, return null.
|
||||
*
|
||||
* @param path The path to get the value from.
|
||||
* @param type The type class of the value
|
||||
* @param <T> The type of the value
|
||||
* @return The value at the path, or the default value if not found.
|
||||
*/
|
||||
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> type) {
|
||||
return get(path, null, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the path using a parser function,
|
||||
* if the path does not exist, return NULL.
|
||||
*
|
||||
* @param path The path to get the value from.
|
||||
* @param parser The function to parse the value
|
||||
* @param <T> The type of the value
|
||||
* @return The value at the path, or null if not found.
|
||||
*/
|
||||
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
|
||||
return get(path, null, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the path for specific type,
|
||||
* if the path does not exist, return the default value.
|
||||
*
|
||||
* @param path The path to get the value from.
|
||||
* @param defaults The default value to return if the path does not exist.
|
||||
* @param clazz The type class of the value
|
||||
* @param <T> The type of the value
|
||||
* @return The value at the path, or the default value if not found.
|
||||
*/
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) {
|
||||
return get(path, defaults, DataFunction.castObject(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the path using a parser function,
|
||||
* if the path does not exist, return the default value.
|
||||
*
|
||||
* @param path The path to get the value from.
|
||||
* @param defaultValue The default value to return if the path does not exist.
|
||||
* @param parser The function to parse the value
|
||||
* @param <T> The type of the value
|
||||
* @return The value at the path, or the default value if not found.
|
||||
*/
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||
@NotNull DataFunction<Object, T> parser) {
|
||||
Object value = get(path);
|
||||
if (value != null) {
|
||||
try {
|
||||
return parser.handle(value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Boolean}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a boolean, false otherwise.
|
||||
*/
|
||||
default boolean isBoolean(@NotNull String path) {
|
||||
return isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Boolean} from the specified path.
|
||||
*
|
||||
* @param path The path to get the boolean.
|
||||
* @return The boolean if the path exists and is a boolean, otherwise false.
|
||||
*/
|
||||
default boolean getBoolean(@NotNull String path) {
|
||||
return getBoolean(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Boolean} from the specified path.
|
||||
*
|
||||
* @param path The path to get the boolean.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The boolean if the path exists and is a boolean, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||
return get(path, def, DataFunction.booleanValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Byte}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a byte, false otherwise.
|
||||
*/
|
||||
default @Nullable Boolean isByte(@NotNull String path) {
|
||||
return isType(path, Byte.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Byte} from the specified path.
|
||||
*
|
||||
* @param path The path to get the byte.
|
||||
* @return The byte if the path exists and is a byte, otherwise 0.
|
||||
*/
|
||||
default @Nullable Byte getByte(@NotNull String path) {
|
||||
return getByte(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Byte} from the specified path.
|
||||
*
|
||||
* @param path The path to get the byte.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The byte if the path exists and is a byte, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||
return get(path, def, DataFunction.byteValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Short}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a short, false otherwise.
|
||||
*/
|
||||
default boolean isShort(@NotNull String path) {
|
||||
return isType(path, Short.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Short} from the specified path.
|
||||
*
|
||||
* @param path The path to get the short.
|
||||
* @return The short if the path exists and is a short, otherwise 0.
|
||||
*/
|
||||
default @Nullable Short getShort(@NotNull String path) {
|
||||
return getShort(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Short} from the specified path.
|
||||
*
|
||||
* @param path The path to get the short.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The short if the path exists and is a short, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||
return get(path, def, DataFunction.shortValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Integer}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is an int, false otherwise.
|
||||
*/
|
||||
default boolean isInt(@NotNull String path) {
|
||||
return isType(path, Integer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Integer} from the specified path.
|
||||
*
|
||||
* @param path The path to get the int.
|
||||
* @return The int if the path exists and is an int, otherwise 0.
|
||||
*/
|
||||
default @Nullable Integer getInt(@NotNull String path) {
|
||||
return getInt(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Integer} from the specified path.
|
||||
*
|
||||
* @param path The path to get the int.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The int if the path exists and is an int, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||
return get(path, def, DataFunction.intValue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Long}.F
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a long, false otherwise.
|
||||
*/
|
||||
default boolean isLong(@NotNull String path) {
|
||||
return isType(path, Long.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Long} from the specified path.
|
||||
*
|
||||
* @param path The path to get the long.
|
||||
* @return The long if the path exists and is a long, otherwise 0.
|
||||
*/
|
||||
default @Nullable Long getLong(@NotNull String path) {
|
||||
return getLong(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Long} from the specified path.
|
||||
*
|
||||
* @param path The path to get the long.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The long if the path exists and is a long, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||
return get(path, def, DataFunction.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Float}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a float, false otherwise.
|
||||
*/
|
||||
default boolean isFloat(@NotNull String path) {
|
||||
return isType(path, Float.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Float} from the specified path.
|
||||
*
|
||||
* @param path The path to get the float.
|
||||
* @return The float if the path exists and is a float, otherwise 0.
|
||||
*/
|
||||
default @Nullable Float getFloat(@NotNull String path) {
|
||||
return getFloat(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Float} from the specified path.
|
||||
*
|
||||
* @param path The path to get the float.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The float if the path exists and is a float, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||
return get(path, def, DataFunction.floatValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Double}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a double, false otherwise.
|
||||
*/
|
||||
default boolean isDouble(@NotNull String path) {
|
||||
return isType(path, Double.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Double} from the specified path.
|
||||
*
|
||||
* @param path The path to get the double.
|
||||
* @return The double if the path exists and is a double, otherwise 0.
|
||||
*/
|
||||
default @Nullable Double getDouble(@NotNull String path) {
|
||||
return getDouble(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Double} from the specified path.
|
||||
*
|
||||
* @param path The path to get the double.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The double if the path exists and is a double, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||
return get(path, def, DataFunction.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link Character}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a char, false otherwise.
|
||||
*/
|
||||
default boolean isChar(@NotNull String path) {
|
||||
return isType(path, Character.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Character} from the specified path.
|
||||
*
|
||||
* @param path The path to get the char.
|
||||
* @return The char if the path exists and is a char, otherwise null.
|
||||
*/
|
||||
default @Nullable Character getChar(@NotNull String path) {
|
||||
return getChar(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link Character} from the specified path.
|
||||
*
|
||||
* @param path The path to get the char.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The char if the path exists and is a char, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||
return get(path, def, Character.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate the value of given path is a {@link String}.
|
||||
*
|
||||
* @param path The path to check.
|
||||
* @return True if the value is present and is a string, false otherwise.
|
||||
*/
|
||||
default boolean isString(@NotNull String path) {
|
||||
return isType(path, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link String} from the specified path.
|
||||
*
|
||||
* @param path The path to get the string.
|
||||
* @return The string if the path exists and is a string, otherwise null.
|
||||
*/
|
||||
default @Nullable String getString(@NotNull String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a {@link String} from the specified path.
|
||||
*
|
||||
* @param path The path to get the string.
|
||||
* @param def The default value to return if the path does not exist.
|
||||
* @return The string if the path exists and is a string, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||
return get(path, def, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of values from current section
|
||||
* <p>
|
||||
* If the path does not exist, an empty list will be returned
|
||||
* <br>Any changes please use {@link #set(String, Object)} after changes
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @param parser The function to parse the values
|
||||
* @param <V> The type of the values
|
||||
* @return The list of values
|
||||
*/
|
||||
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
|
||||
return getCollection(path, ArrayList::new, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of strings from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of strings
|
||||
*/
|
||||
default @NotNull List<String> getStringList(@NotNull String path) {
|
||||
return getList(path, DataFunction.castToString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of integer from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of int values
|
||||
*/
|
||||
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
||||
return getList(path, DataFunction.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of long from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of long values
|
||||
*/
|
||||
default @NotNull List<Long> getLongList(@NotNull String path) {
|
||||
return getList(path, DataFunction.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of double from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of doubles
|
||||
*/
|
||||
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
||||
return getList(path, DataFunction.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of floats from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of floats
|
||||
*/
|
||||
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
||||
return getList(path, DataFunction.floatValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of bytes from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of bytes
|
||||
*/
|
||||
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
||||
return getList(path, DataFunction.byteValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of char from current section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of char
|
||||
*/
|
||||
default @NotNull List<Character> getCharList(@NotNull String path) {
|
||||
return getList(path, DataFunction.castObject(Character.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of {@link ConfigureSection} from current section
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of {@link ConfigureSection}
|
||||
*/
|
||||
default @NotNull List<ConfigureSection> getSectionList(@NotNull String path) {
|
||||
return getList(path, obj -> {
|
||||
if (obj instanceof ConfigureSection) {
|
||||
return (ConfigureSection) obj;
|
||||
} else if (obj instanceof Map) {
|
||||
return createSection(childPath(path), (Map<?, ?>) obj);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specific type of collection from current section.
|
||||
*
|
||||
* @param path The path to get the collection from
|
||||
* @param constructor The constructor of the collection
|
||||
* @param parser The function to parse the values
|
||||
* @param <T> The type of the values
|
||||
* @param <C> The type of the collection
|
||||
* @return The collection of values
|
||||
*/
|
||||
default <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path,
|
||||
@NotNull Supplier<C> constructor,
|
||||
@NotNull DataFunction<Object, T> parser) {
|
||||
return parseCollection(getList(path), constructor, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specific type of steam from current section.
|
||||
*
|
||||
* @param path The path to get the stream from
|
||||
* @return The stream of values
|
||||
*/
|
||||
default @NotNull Stream<?> stream(@NotNull String path) {
|
||||
List<?> values = getList(path);
|
||||
return values == null ? Stream.empty() : values.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specific type of steam from current section.
|
||||
*
|
||||
* @param path The path to get the stream from
|
||||
* @param parser The function to parse the values
|
||||
* @param <T> The type of the values
|
||||
* @return The stream of values
|
||||
*/
|
||||
default <T> @NotNull Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
|
||||
return stream(path).map(parser);
|
||||
}
|
||||
|
||||
default String childPath(String path) {
|
||||
int index = path.indexOf(pathSeparator());
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
||||
@Nullable List<?> data,
|
||||
@NotNull Supplier<C> constructor,
|
||||
@NotNull DataFunction<Object, T> parser
|
||||
) {
|
||||
C values = constructor.get();
|
||||
if (data == null) return values;
|
||||
for (Object obj : data) {
|
||||
try {
|
||||
values.add(parser.handle(obj));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package cc.carm.lib.configuration.source.section;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* ConfigureSource represents the source of configuration,
|
||||
* which can be a file, a database, or any other source.
|
||||
*
|
||||
* @param <SECTION> The type of the root section.
|
||||
* @param <ORIGINAL> The original configuration object.
|
||||
* @param <SELF> The type of the source itself, for further implement support.
|
||||
* @see ConfigureSection
|
||||
*/
|
||||
public abstract class ConfigureSource<
|
||||
SECTION extends ConfigureSection, ORIGINAL,
|
||||
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
|
||||
implements ConfigureSection {
|
||||
|
||||
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
|
||||
protected long lastUpdateMillis;
|
||||
|
||||
protected ConfigureSource(@NotNull ConfigurationHolder<? extends SELF> holder, long lastUpdateMillis) {
|
||||
this.holder = holder;
|
||||
this.lastUpdateMillis = lastUpdateMillis;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<? extends SELF> holder() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
public void reload() throws Exception {
|
||||
onReload(); // 调用重写的Reload方法
|
||||
this.lastUpdateMillis = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@ApiStatus.Internal
|
||||
protected abstract @NotNull SELF self();
|
||||
|
||||
/**
|
||||
* @return The original configuration object.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public abstract @NotNull ORIGINAL original();
|
||||
|
||||
/**
|
||||
* @return The root {@link ConfigureSection}, which represents the entire configuration.
|
||||
*/
|
||||
public abstract @NotNull SECTION section();
|
||||
|
||||
/**
|
||||
* Save the whole configuration.
|
||||
*
|
||||
* @throws Exception If any error occurs while saving.
|
||||
*/
|
||||
public abstract void save() throws Exception;
|
||||
|
||||
/**
|
||||
* Reload the configuration.
|
||||
* <br>This used for implementation, for external usage, use {@link #reload()}
|
||||
*
|
||||
* @throws Exception If any error occurs while reloading.
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
protected abstract void onReload() throws Exception;
|
||||
|
||||
public char pathSeparator() {
|
||||
return holder().options().get(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
public long getLastUpdateMillis() {
|
||||
return this.lastUpdateMillis;
|
||||
}
|
||||
|
||||
public boolean isExpired(long parsedTime) {
|
||||
return getLastUpdateMillis() > parsedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source also represents the root section, so it has no parent
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
@Override
|
||||
@Contract(pure = true, value = "->null")
|
||||
public @Nullable ConfigureSection parent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String path() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
return section().getValues(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||
return section().getKeys(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
|
||||
return section().asMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||
return section().createSection(path, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
section().set(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull String path) {
|
||||
section().remove(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return section().get(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package cc.carm.lib.configuration.value;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a configurable value with type safety and null-handling capabilities.
|
||||
* <p>
|
||||
* This abstract class provides core functionalities for managing configuration values,
|
||||
* including value retrieval with fallback defaults, null safety enforcement, and value
|
||||
* persistence controls. It serves as the foundation for type-specific configuration
|
||||
* implementations.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Functions:</h3>
|
||||
* <ul>
|
||||
* <li>Type-safe value access through {@link #get()} and {@link #optional()}</li>
|
||||
* <li>Default value fallback via {@link #getOrDefault()}</li>
|
||||
* <li>Null-safety enforcement with {@link #resolve()} and {@link #getNotNull()}</li>
|
||||
* <li>Default value initialization through {@link #setDefault()}</li>
|
||||
* <li>Value comparison with {@link #isDefault()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Persistence Behavior:</h3>
|
||||
* Value modifications via {@link #set(Object)} or {@link #setDefault()} methods
|
||||
* <b>do NOT automatically persist</b> to configuration sources. Explicit calls to
|
||||
* {@link ConfigurationHolder#save()} are required for permanent storage.
|
||||
*
|
||||
* @see ValueManifest Base class providing metadata and default value handling
|
||||
* @see ConfigurationHolder Responsible for configuration source persistence
|
||||
*/
|
||||
public abstract class ConfigValue<T, U> extends ValueManifest<T, U> {
|
||||
|
||||
protected ConfigValue(@NotNull ValueManifest<T, U> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured value (i.e., the value read from the source).
|
||||
* <br> If no default value was written during initialization, you can use
|
||||
* the {@link #getOrDefault()} method to obtain the default value when this value is empty.
|
||||
*
|
||||
* @return Configured value
|
||||
*/
|
||||
public abstract @Nullable T get();
|
||||
|
||||
/**
|
||||
* Gets the configured value, or returns the default value if not present.
|
||||
*
|
||||
* @return Configured value or default value
|
||||
*/
|
||||
public T getOrDefault() {
|
||||
return getOr(defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured value, or returns the specified default value if not present.
|
||||
*
|
||||
* @param defaults The default value to return if the configured value is not present
|
||||
* @return Configured value or specified default value
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
public T getOr(T defaults) {
|
||||
return optional().orElse(defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-null value of this configuration.
|
||||
*
|
||||
* @return Non-null value
|
||||
* @throws NullPointerException Thrown when the corresponding data is null
|
||||
*/
|
||||
public @NotNull T resolve() {
|
||||
return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-null value of this configuration.
|
||||
*
|
||||
* @return Non-null value
|
||||
* @throws NullPointerException Thrown when the corresponding data is null
|
||||
* @see #resolve() for a more descriptive function
|
||||
*/
|
||||
public @NotNull T getNotNull() {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this configuration as an {@link Optional}.
|
||||
*
|
||||
* @return {@link Optional} value
|
||||
*/
|
||||
public @NotNull Optional<@Nullable T> optional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this configuration.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*
|
||||
* @param value The value to set
|
||||
*/
|
||||
public abstract void set(@Nullable T value);
|
||||
|
||||
/**
|
||||
* Initializes the default value for this configuration.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*/
|
||||
public void setDefault() {
|
||||
setDefault(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration value to its default.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*
|
||||
* @param override Whether to overwrite existing configured value
|
||||
*/
|
||||
public void setDefault(boolean override) {
|
||||
if (config().contains(path()) && !override) return; // Skip if the value is already set
|
||||
set(defaults()); // Set the default value
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the loaded configuration value matches the default value.
|
||||
*
|
||||
* @return Whether the current value is the default value
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
return Objects.equals(defaults(), get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to save the configuration.
|
||||
* <br>To save multiple modifications,
|
||||
* it is recommended to call {@link ConfigurationHolder#save()}
|
||||
* after all modifications are completed instead of this.
|
||||
*
|
||||
* @throws Exception Thrown when an error occurs during saving
|
||||
*/
|
||||
public void save() throws Exception {
|
||||
holder().save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package cc.carm.lib.configuration.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueValidator;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ValueManifest<TYPE, UNIT> {
|
||||
|
||||
protected final @NotNull ValueType<TYPE> type;
|
||||
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
|
||||
|
||||
protected @Nullable ConfigurationHolder<?> holder;
|
||||
protected @Nullable String path; // Section path
|
||||
|
||||
protected @NotNull ValueValidator<UNIT> validator;
|
||||
protected @NotNull Supplier<@Nullable TYPE> defaultSupplier;
|
||||
|
||||
public ValueManifest(@NotNull ValueType<TYPE> type) {
|
||||
this(type, () -> null, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull TYPE defaultValue) {
|
||||
this(ValueType.of(defaultValue), () -> defaultValue);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier) {
|
||||
this(type, defaultSupplier, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<TYPE> type,
|
||||
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||
@NotNull ValueValidator<UNIT> validator) {
|
||||
this(type, defaultSupplier, validator, EMPTY_INITIALIZER, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||
@NotNull ValueValidator<UNIT> validator,
|
||||
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
|
||||
this(type, defaultSupplier, validator, initializer, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<TYPE> type,
|
||||
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||
@NotNull ValueValidator<UNIT> validator,
|
||||
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
|
||||
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
|
||||
this.type = type;
|
||||
this.validator = validator;
|
||||
this.initializer = initializer;
|
||||
this.defaultSupplier = defaultSupplier;
|
||||
this.holder = holder;
|
||||
this.path = path;
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected ValueManifest(@NotNull ValueManifest<TYPE, UNIT> manifest) {
|
||||
this(manifest.type, manifest.defaultSupplier, manifest.validator, manifest.initializer, manifest.holder, manifest.path);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
|
||||
this.holder = holder;
|
||||
this.path = path;
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
if (holder != null && path != null) this.initializer.accept(holder, path);
|
||||
}
|
||||
|
||||
public @NotNull ValueType<TYPE> type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void holder(@NotNull ConfigurationHolder<?> holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
public void path(@NotNull String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public @Nullable TYPE defaults() {
|
||||
return this.defaultSupplier.get();
|
||||
}
|
||||
|
||||
public void defaults(@Nullable TYPE defaultValue) {
|
||||
defaults(() -> defaultValue);
|
||||
}
|
||||
|
||||
public void defaults(@NotNull Supplier<@Nullable TYPE> defaultValue) {
|
||||
this.defaultSupplier = defaultValue;
|
||||
}
|
||||
|
||||
public boolean hasDefaults() {
|
||||
return defaults() != null;
|
||||
}
|
||||
|
||||
public @NotNull ValueValidator<UNIT> validator() {
|
||||
return this.validator;
|
||||
}
|
||||
|
||||
public void validator(@NotNull ValueValidator<UNIT> validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public void validate(@NotNull ValueValidator<UNIT> validator) {
|
||||
validator(this.validator.and(validator));
|
||||
}
|
||||
|
||||
protected UNIT withValidated(@Nullable UNIT value) throws Exception {
|
||||
validator.validate(holder(), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public @NotNull String path() {
|
||||
if (path != null) return path;
|
||||
else throw new IllegalStateException("No section path provided for Value(" + type() + ").");
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<?> holder() {
|
||||
if (this.holder != null) return this.holder;
|
||||
throw new IllegalStateException("Value(" + type() + ") does not have a provider.");
|
||||
}
|
||||
|
||||
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||
return holder().config();
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationMetaHolder metadata() {
|
||||
return holder().metadata(path());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected @Nullable Object getData() {
|
||||
return config().get(path());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected void setData(@Nullable Object value) {
|
||||
config().set(path(), value);
|
||||
}
|
||||
|
||||
protected void throwing(@NotNull Throwable throwable) {
|
||||
throwing(path, throwable);
|
||||
}
|
||||
|
||||
protected void throwing(@NotNull String path, @NotNull Throwable throwable) {
|
||||
if (holder == null) throwable.printStackTrace();
|
||||
else holder.throwing(path, throwable);
|
||||
}
|
||||
|
||||
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cc.carm.lib.configuration.value.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class CachedConfigValue<T, U> extends ConfigValue<T, U> {
|
||||
|
||||
protected @Nullable T cachedValue;
|
||||
protected long parsedTime = -1;
|
||||
|
||||
protected CachedConfigValue(@NotNull ValueManifest<T, U> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
protected T updateCache(T value) {
|
||||
this.parsedTime = System.currentTimeMillis();
|
||||
this.cachedValue = value;
|
||||
return getCachedValue();
|
||||
}
|
||||
|
||||
public @Nullable T getCachedValue() {
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
public boolean cacheExpired() {
|
||||
return this.parsedTime <= 0 || config().isExpired(this.parsedTime);
|
||||
}
|
||||
|
||||
protected final T getDefaultFirst(@Nullable T value) {
|
||||
return updateCache(this.defaults() == null ? value : this.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached value or the default value if the cached value is null
|
||||
*
|
||||
* @return the cached value or the default value
|
||||
*/
|
||||
protected @Nullable T getCachedOrDefault() {
|
||||
return getCachedOrDefault(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached value or the default value if the cached value is null
|
||||
*
|
||||
* @param emptyValue the value to return if the cached value and the default value are null
|
||||
* @return the cached value or the default value
|
||||
*/
|
||||
@Contract("!null->!null")
|
||||
protected T getCachedOrDefault(@Nullable T emptyValue) {
|
||||
if (getCachedValue() != null) return getCachedValue();
|
||||
else if (defaults() != null) return defaults();
|
||||
else return emptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter Value adapter
|
||||
* @param <O> Base object type
|
||||
* @return Value's parser, parse base object to value.
|
||||
*/
|
||||
protected <O> @Nullable ValueParser<O> parserFor(@NotNull ValueAdapter<O> adapter) {
|
||||
if (adapter.parser() != null) return adapter.parser();
|
||||
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||
if (registered == null) return null;
|
||||
return registered.parser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter Value adapter
|
||||
* @param <O> Base object type
|
||||
* @return Value's serializer, parse value to base object.
|
||||
*/
|
||||
protected <O> @Nullable ValueSerializer<O> serializerFor(@NotNull ValueAdapter<O> adapter) {
|
||||
if (adapter.serializer() != null) return adapter.serializer();
|
||||
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||
if (registered == null) return null;
|
||||
return registered.serializer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package cc.carm.lib.configuration.value.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Base implementation of a collection config value, like {@link List} or {@link Set}.
|
||||
*
|
||||
* @param <V> Value type
|
||||
* @param <C> Collection type
|
||||
* @param <SELF> Self reference type (used for internal call or recursive generics)
|
||||
*/
|
||||
public abstract class CollectionConfigValue<
|
||||
V, C extends Collection<V>,
|
||||
SELF extends CollectionConfigValue<V, C, SELF>
|
||||
> extends CachedConfigValue<C, V> implements Collection<V> {
|
||||
|
||||
protected final @NotNull Supplier<? extends C> constructor;
|
||||
protected final @NotNull ValueAdapter<V> paramAdapter;
|
||||
|
||||
public CollectionConfigValue(@NotNull ValueManifest<C, V> manifest,
|
||||
@NotNull Supplier<? extends C> constructor,
|
||||
@NotNull ValueAdapter<V> paramAdapter) {
|
||||
super(manifest);
|
||||
this.constructor = constructor;
|
||||
this.paramAdapter = paramAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Adapter of this value.
|
||||
*/
|
||||
public @NotNull ValueAdapter<V> adapter() {
|
||||
return this.paramAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<V> paramType() {
|
||||
return adapter().type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's parser, parse base object to value.
|
||||
*/
|
||||
public @Nullable ValueParser<V> parser() {
|
||||
return parserFor(adapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's serializer, parse value to base object.
|
||||
*/
|
||||
public @Nullable ValueSerializer<V> serializer() {
|
||||
return serializerFor(adapter());
|
||||
}
|
||||
|
||||
private @NotNull C createCollection() {
|
||||
return constructor.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull C get() {
|
||||
if (!cacheExpired()) return getCachedOrDefault(createCollection());
|
||||
// Data that is outdated and needs to be parsed again.
|
||||
C set = createCollection();
|
||||
try {
|
||||
List<?> data = config().contains(path()) ? config().getList(path()) : null;
|
||||
if (data == null) return getDefaultFirst(set);
|
||||
|
||||
ValueParser<V> parser = parser();
|
||||
if (parser == null) return getDefaultFirst(set);
|
||||
|
||||
int i = 0;
|
||||
for (Object dataVal : data) {
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
set.add(withValidated(parser.parse(holder(), paramType(), dataVal)));
|
||||
} catch (Exception e) {
|
||||
throwing(path + "[" + i + "]", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throwing(ex);
|
||||
}
|
||||
return updateCache(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable C collection) {
|
||||
updateCache(collection);
|
||||
if (collection == null) {
|
||||
setData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ValueSerializer<V> serializer = serializer();
|
||||
if (serializer == null) return;
|
||||
|
||||
List<Object> data = new ArrayList<>();
|
||||
for (V val : collection) {
|
||||
if (val == null) continue;
|
||||
try {
|
||||
data.add(serializer.serialize(holder(), paramType(), withValidated(val)));
|
||||
} catch (Exception ex) {
|
||||
throwing(ex);
|
||||
}
|
||||
}
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public @NotNull C copy() {
|
||||
C other = createCollection();
|
||||
other.addAll(resolve());
|
||||
return other;
|
||||
}
|
||||
|
||||
public abstract @NotNull SELF self();
|
||||
|
||||
public <T> @NotNull T handle(Function<C, T> function) {
|
||||
C list = resolve();
|
||||
T result = function.apply(list);
|
||||
set(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull SELF modify(Consumer<C> consumer) {
|
||||
C list = resolve();
|
||||
consumer.accept(list);
|
||||
set(list);
|
||||
return self();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return resolve().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return resolve().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return resolve().contains(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return resolve().iterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object @NotNull [] toArray() {
|
||||
return resolve().toArray();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T @NotNull [] toArray(@NotNull T[] a) {
|
||||
return resolve().toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
return new HashSet<>(resolve()).containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V v) {
|
||||
handle(list -> list.add(v));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return handle(list -> list.remove(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.removeAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.retainAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modify(Collection::clear);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package cc.carm.lib.configuration.value.standard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.collection.SimpleCollectionCreator;
|
||||
import cc.carm.lib.configuration.builder.list.ConfigListCreator;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredList<V> extends CollectionConfigValue<V, List<V>, ConfiguredList<V>> implements List<V> {
|
||||
|
||||
public static <T> @NotNull ConfigListCreator<T> builderOf(@NotNull Class<T> type) {
|
||||
return builderOf(ValueType.of(type));
|
||||
}
|
||||
|
||||
public static <T> @NotNull ConfigListCreator<T> builderOf(@NotNull ValueType<T> type) {
|
||||
return new ConfigListCreator<>(type);
|
||||
}
|
||||
|
||||
public static <T>
|
||||
@NotNull SimpleCollectionCreator.Source<Object, T, List<T>, ConfiguredList<T>> with(@NotNull Class<T> registeredType) {
|
||||
return with(ValueType.of(registeredType));
|
||||
}
|
||||
|
||||
public static <T> @NotNull SimpleCollectionCreator.Source<Object, T, List<T>, ConfiguredList<T>> with(@NotNull ValueType<T> registeredType) {
|
||||
return builderOf(registeredType).fromObject();
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> @NotNull ConfiguredList<T> of(@NotNull T value, @NotNull T... values) {
|
||||
List<T> list = new ArrayList<>();
|
||||
list.add(value);
|
||||
Collections.addAll(list, values);
|
||||
return with(ValueType.of(value)).defaults(list).build();
|
||||
}
|
||||
|
||||
public ConfiguredList(@NotNull ValueManifest<List<V>, V> manifest,
|
||||
@NotNull Supplier<? extends List<V>> constructor,
|
||||
@NotNull ValueAdapter<V> paramAdapter) {
|
||||
super(manifest, constructor, paramAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(int index) {
|
||||
return resolve().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V set(int index, V element) {
|
||||
return handle(list -> list.set(index, element));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void add(int index, V element) {
|
||||
modify(list -> list.add(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(index, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
return handle(list -> list.remove(index));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return resolve().indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return resolve().lastIndexOf(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator() {
|
||||
return resolve().listIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator(int index) {
|
||||
return resolve().listIterator(index);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<V> subList(int fromIndex, int toIndex) {
|
||||
return resolve().subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package cc.carm.lib.configuration.value.standard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.map.ConfigMapCreator;
|
||||
import cc.carm.lib.configuration.builder.map.SourceMapBuilder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> implements Map<K, V> {
|
||||
|
||||
public static <V> ConfigMapCreator<String, V> builderOf(@NotNull Class<V> valueType) {
|
||||
return builderOf(String.class, valueType);
|
||||
}
|
||||
|
||||
public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
return new ConfigMapCreator<>(keyType, valueType);
|
||||
}
|
||||
|
||||
public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull Class<K> keyType, @NotNull Class<V> valueType) {
|
||||
return new ConfigMapCreator<>(ValueType.of(keyType), ValueType.of(valueType));
|
||||
}
|
||||
|
||||
public static @NotNull <K, V>
|
||||
SourceMapBuilder<LinkedHashMap<K, V>, Object, K, V> with(@NotNull Class<K> keyType, @NotNull Class<V> valueType) {
|
||||
return with(ValueType.of(keyType), ValueType.of(valueType));
|
||||
}
|
||||
|
||||
public static @NotNull <K, V>
|
||||
SourceMapBuilder<LinkedHashMap<K, V>, Object, K, V> with(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
return new ConfigMapCreator<>(keyType, valueType).asLinkedMap().fromObject();
|
||||
}
|
||||
|
||||
public static <K, V> ConfiguredMap<K, V> of(@NotNull Supplier<? extends Map<K, V>> constructor,
|
||||
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
|
||||
return new ConfiguredMap<>(new ValueManifest<>(new ValueType<Map<K, V>>() {
|
||||
}, constructor::get), constructor, keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends Map<K, V>> constructor;
|
||||
|
||||
protected final @NotNull ValueAdapter<K> keyAdapter;
|
||||
protected final @NotNull ValueAdapter<V> valueAdapter;
|
||||
|
||||
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>, V> manifest,
|
||||
@NotNull Supplier<? extends Map<K, V>> constructor,
|
||||
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
|
||||
super(manifest);
|
||||
this.constructor = constructor;
|
||||
this.keyAdapter = keyAdapter;
|
||||
this.valueAdapter = valueAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> keyAdapter() {
|
||||
return keyAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<K> keyType() {
|
||||
return keyAdapter().type();
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<V> valueAdapter() {
|
||||
return valueAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<V> valueType() {
|
||||
return valueAdapter().type();
|
||||
}
|
||||
|
||||
private Map<K, V> createMap() {
|
||||
return this.constructor.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<K, V> get() {
|
||||
if (!cacheExpired()) return getCachedOrDefault(createMap());
|
||||
// If the value is expired, we need to update it
|
||||
Map<K, V> map = createMap();
|
||||
|
||||
try {
|
||||
ConfigureSection section = config().getSection(path());
|
||||
if (section == null) return getDefaultFirst(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
if (keys.isEmpty()) return getDefaultFirst(map);
|
||||
|
||||
ValueParser<K> keyParser = parserFor(keyAdapter);
|
||||
if (keyParser == null) return getDefaultFirst(map);
|
||||
ValueParser<V> valueParser = parserFor(valueAdapter);
|
||||
if (valueParser == null) return getDefaultFirst(map);
|
||||
|
||||
for (String dataKey : keys) {
|
||||
Object dataVal = section.get(dataKey);
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
K key = keyParser.parse(holder(), keyType(), dataKey);
|
||||
V value = valueParser.parse(holder(), valueType(), dataVal);
|
||||
map.put(key, withValidated(value));
|
||||
} catch (Exception e) {
|
||||
throwing(path + "." + dataKey, e);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throwing(ex);
|
||||
}
|
||||
|
||||
return updateCache(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return get().get(key);
|
||||
}
|
||||
|
||||
public V getNotNull(@Nullable K key) {
|
||||
return Objects.requireNonNull(get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable Map<K, V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) {
|
||||
setData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
|
||||
if (keySerializer == null) return;
|
||||
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
|
||||
if (valueSerializer == null) return;
|
||||
|
||||
Map<Object, Object> data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<K, V> entry : value.entrySet()) {
|
||||
try {
|
||||
data.put(
|
||||
keySerializer.serialize(holder(), keyType(), entry.getKey()),
|
||||
valueSerializer.serialize(holder(), valueType(), withValidated(entry.getValue()))
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throwing(path + "." + entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
setData(data);
|
||||
} catch (Exception ex) {
|
||||
throwing(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
|
||||
Map<K, V> m = get();
|
||||
T result = function.apply(m);
|
||||
set(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull ConfiguredMap<K, V> modify(Consumer<Map<K, V>> consumer) {
|
||||
Map<K, V> m = get();
|
||||
consumer.accept(m);
|
||||
set(m);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return get().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return get().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return get().containsValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return handle(m -> m.put(key, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return handle(m -> m.remove(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||
modify(map -> map.putAll(m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modify(Map::clear);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return get().keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return get().values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Unmodifiable
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return get().entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user