1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-05 02:58:20 +08:00

Compare commits

..

53 Commits

Author SHA1 Message Date
carm 0bfd15aaad feat(record): Support record type values adapter. 2025-07-02 11:04:27 +08:00
carm 15f395a8e0 test(record): Add testcase for nullable values. 2025-06-26 03:04:21 +08:00
carm a61040a0e2 ci(jdk): Using JDK 21. 2025-06-26 02:28:32 +08:00
carm 8766b4d77b feat(adapter): Support complex ParameterizedType's serialize & deserialize. 2025-06-26 02:24:42 +08:00
carm 8c1214612a feat(adapter): Support complex ParameterizedType's serialize & deserialize. 2025-06-25 09:31:07 +08:00
carm 608d92f834 feat(record): Support record type values adapter. 2025-06-25 07:10:26 +08:00
carm ad6ab9eb36 feat(temp): Add a "temporary source" for Testing/FastDev cases. (Use default config in codes first.) 2025-06-25 07:10:04 +08:00
dependabot[bot] 0eda8d8a0f build(deps-dev): bump log4j.version from 2.24.3 to 2.25.0
Bumps `log4j.version` from 2.24.3 to 2.25.0.

Updates `org.apache.logging.log4j:log4j-api` from 2.24.3 to 2.25.0

Updates `org.apache.logging.log4j:log4j-core` from 2.24.3 to 2.25.0

Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.24.3 to 2.25.0

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-api
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: org.apache.logging.log4j:log4j-slf4j-impl
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-25 06:29:38 +08:00
dependabot[bot] b19691b5a4 build(deps): bump org.sonatype.central:central-publishing-maven-plugin
Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.7.0 to 0.8.0.
- [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits)

---
updated-dependencies:
- dependency-name: org.sonatype.central:central-publishing-maven-plugin
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-25 06:29:28 +08:00
renovate[bot] c7331aa556 fix(deps): update kotlin monorepo to v2.2.0 2025-06-25 06:29:18 +08:00
carm a440c050e5 feat(type): Add ofMap(K,V) method for ValueType 2025-06-23 15:27:52 +08:00
renovate[bot] b9d45a9bb9 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.5.1 2025-06-10 22:45:19 +08:00
carm 67482de7a9 docs: drop visitors counter 2025-05-14 04:25:23 +08:00
carm 04bedbef07 style: Reformatted code with .editorconfig 2025-05-14 04:24:56 +08:00
carm a4abfb733a style: Reformatted code with .editorconfig 2025-05-14 04:22:48 +08:00
carm 76d276436b ci(deploy): Skip deployment for demo project. 2025-05-14 03:55:54 +08:00
carm e7198f22d5 ci(deploy): Skip deployment for demo project. 2025-05-14 03:48:54 +08:00
carm 5bd20e173f ci(deploy): Skip deployment for demo project. 2025-05-14 03:41:15 +08:00
carm 15c4bb13e8 build(repo): Use official repositories. 2025-05-14 03:33:45 +08:00
dependabot[bot] b74eb5c035 build(deps): bump com.google.code.gson:gson from 2.13.0 to 2.13.1
Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.13.0 to 2.13.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.13.0...gson-parent-2.13.1)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 03:27:17 +08:00
renovate[bot] ed3a68af1e fix(deps): update kotlin monorepo to v2.1.21 2025-05-14 03:25:58 +08:00
dependabot[bot] 447b82c880 build(deps): bump org.mongodb:mongodb-driver-sync from 5.4.0 to 5.5.0
Bumps [org.mongodb:mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/mongodb/mongo-java-driver/releases)
- [Commits](https://github.com/mongodb/mongo-java-driver/compare/r5.4.0...r5.5.0)

---
updated-dependencies:
- dependency-name: org.mongodb:mongodb-driver-sync
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 03:25:49 +08:00
carm 5700f8c1c6 Merge remote-tracking branch 'origin/master' 2025-04-18 00:53:01 +08:00
carm 1cf230d6b6 chore: Bump to 4.1.4 2025-04-18 00:52:51 +08:00
carm 1ffd4b2f0b build(checks): Bye codacy. 2025-04-18 00:31:42 +08:00
dependabot[bot] 92ce780d6f build(deps): bump kotlin.version from 1.9.22 to 2.1.20
Bumps `kotlin.version` from 1.9.22 to 2.1.20.

Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 1.9.22 to 2.1.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.22...v2.1.20)

Updates `org.jetbrains.kotlin:kotlin-maven-plugin` from 1.9.22 to 2.1.20

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
  dependency-version: 2.1.20
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin
  dependency-version: 2.1.20
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-18 00:30:08 +08:00
carm a2de3303e8 feat(section): Add more section functions 2025-04-17 23:55:15 +08:00
renovate[bot] 47a1981002 fix(deps): update dependency com.google.code.gson:gson to v2.13.0 2025-04-17 22:06:46 +08:00
renovate[bot] b97bc5d1a9 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.3 2025-04-17 22:05:43 +08:00
carm cc927fdc0e Merge pull request #134 from huanmeng-qwq/language/kotlin
feat: Support kotlin dsl
2025-04-17 22:05:28 +08:00
carm 5dc2693106 build(doc): Removed javadoc generation for demo project. 2025-04-17 18:44:48 +08:00
carm 472ce66ca7 build(kotlin): Fixed version 2025-04-17 18:13:00 +08:00
carm 796771554f Merge branch 'master' into language/kotlin 2025-04-17 17:48:38 +08:00
carm 28cb5d4c83 refactor(demo): Move all tests to demo. 2025-04-17 17:43:29 +08:00
carm a670aec986 fix(deps): Fixed the missing kotlin version. 2025-03-30 22:48:19 +08:00
renovate[bot] 51c54b9b53 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.4.0 2025-03-23 01:10:53 +08:00
huanmeng-qwq fa99385ff0 feat: Support kotlin dsl 2025-03-22 17:43:48 +08:00
carm 70a3b893a6 chore(text): Add missing text functions 2025-03-21 03:30:47 +08:00
carm c32a941906 docs: Edit logo size 2025-03-21 02:32:33 +08:00
carm 8b597d10f9 feat: Optimized ContentHandler with Replacer & Inserter. 2025-03-21 02:31:04 +08:00
carm cedfcfd262 docs: Update banners and icons 2025-03-21 02:30:35 +08:00
carm 12add75232 feat: Enhanced text content replacer 2025-03-20 20:28:33 +08:00
carm 4d29a24768 docs: Edited readme 2025-03-20 17:16:33 +08:00
carm 8626b86961 chore: Change logo 2025-03-20 16:08:17 +08:00
carm 754dc1de1f feat: Change logo 2025-03-20 16:04:50 +08:00
carm 3d1d4ca609 chore: Add logo & icon. 2025-03-20 15:32:27 +08:00
carm 990b10b6f0 chore: Add logo & icon. 2025-03-20 15:30:34 +08:00
carm 7462cd720e chore: Optimize code format & functions. 2025-03-19 18:36:08 +08:00
carm 6bc83eb3b3 chore: Optimize code format & functions. 2025-03-19 18:35:45 +08:00
carm dbec551c84 chore: Fixed the param errors. 2025-03-17 01:39:08 +08:00
carm 82cca5eca2 feat(exception): Supported ConfigExceptionHandler for holders. 2025-03-17 01:06:57 +08:00
carm 65f3cc1b3d feat(validator): Support validators for config values.
BREAKING CHANGE: `ValueManifest` and `ConfigValue` added a new type "UNIT" to mark the minimal unit value of this instance.

link #132
2025-03-17 00:26:47 +08:00
carm fae048dd69 feat(validator): Support validators for config values.
BREAKING CHANGE: `ValueManifest` and `ConfigValue` added a new type "UNIT" to mark the minimal unit value of this instance.

link #132
2025-03-16 23:56:36 +08:00
132 changed files with 3497 additions and 1021 deletions
+4 -1
View File
@@ -1,3 +1,6 @@
# configured Javadoc # configured Javadoc
Based on [Github Pages](https://pages.github.com/), please see [JavaDoc](https://carmjos.github.io/configured) 。 Based
on [Github Pages](https://pages.github.com/),
please
see [JavaDoc](https://carmjos.github.io/configured) 。
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

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

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

+346
View File
@@ -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
+47 -14
View File
@@ -1,37 +1,70 @@
--- ---
name: 问题提交 name:
about: 描述问题提交,帮助我们对其进行检查与修复。 问题提交
about:
描述问题并提交,帮助我们对其进行检查与修复。
title: '' title: ''
labels: bug labels:
bug
assignees: '' assignees: ''
--- ---
### **问题简述** ###
*
*问题简述
**
用简短的话语描述一下大概问题。 用简短的话语描述一下大概问题。
### **问题来源** ###
*
*问题来源
**
描述一下通过哪些操作才发现的问题,如: 描述一下通过哪些操作才发现的问题,如:
1. 使用了 '...' 1.
2. 输入了 '....' 使用了 '...'
3. 出现了报错 '....' 2.
输入了 '....'
3.
出现了报错 '....'
### **预期结果** (可选) ###
*
*预期结果
** (可选)
如果问题不发生,应该是什么情况 如果问题不发生,应该是什么情况
### **问题截图/问题报错** ###
*
*问题截图/问题报错
**
如果有报错或输出,请提供截图。 如果有报错或输出,请提供截图。
### **操作环境** ###
*
*操作环境
**
- 系统环境: `Windows 10` / `Ubuntu` / `...` -
- Java版本: `JDK11` / `OPENJDK8` / `JRE8` / `...` 系统环境:
`Windows 10` /
`Ubuntu` /
`...`
-
Java版本:
`JDK11` /
`OPENJDK8` /
`JRE8` /
`...`
### **其他补充** ###
*
*其他补充
**
如有其他补充,可以在这里描述。 如有其他补充,可以在这里描述。
+22 -7
View File
@@ -1,23 +1,38 @@
--- ---
name: 功能需求 name:
about: 希望我们提供更多的功能。 功能需求
about:
希望我们提供更多的功能。
title: '' title: ''
labels: enhancement labels:
enhancement
assignees: '' assignees: ''
--- ---
### **功能简述** ###
*
*功能简述
**
简单的描述一下你想要的功能 简单的描述一下你想要的功能
### **需求来源** ###
*
*需求来源
**
简单的描述一下为什么需要这个功能。 简单的描述一下为什么需要这个功能。
### **功能参考**(可选) ###
*
*功能参考
**(可选)
如果有相关功能的参考,如文本、截图,请提供给我们。 如果有相关功能的参考,如文本、截图,请提供给我们。
### **附加内容** ###
*
*附加内容
**
如果有什么小细节需要重点注意,请在这里告诉我们。 如果有什么小细节需要重点注意,请在这里告诉我们。
-54
View File
@@ -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@v4
# 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@09916000460adeeedc96b9704f86deba53e2ad5d
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@v3
with:
sarif_file: results.sarif
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
- name: "Set up JDK" - name: "Set up JDK"
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '11' java-version: '21'
distribution: 'adopt' distribution: 'adopt'
cache: maven cache: maven
server-id: github server-id: github
@@ -91,7 +91,7 @@ jobs:
- name: "Set up JDK" - name: "Set up JDK"
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '11' java-version: '21'
distribution: 'adopt' distribution: 'adopt'
cache: maven cache: maven
server-id: central server-id: central
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: "Set up JDK" - name: "Set up JDK"
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: '11' java-version: '21'
distribution: 'adopt' distribution: 'adopt'
- name: "Package" - name: "Package"
run: mvn -B package --file pom.xml -Dgpg.skip run: mvn -B package --file pom.xml -Dgpg.skip
+2 -1
View File
@@ -1,3 +1,4 @@
.idea/ .idea/*
!.idea/icon.svg
**/target/ **/target/
**.iml **.iml
+36
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

+7 -6
View File
@@ -6,12 +6,13 @@
[![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml) [![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured) [![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured) ![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured)
![](https://visitor-badge.glitch.me/badge?page_id=configured.readme)
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ] README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
</div> </div>
# configured (config-framework) # configured _(config framework)_
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
_**"Once set, Simple get."**_ _**"Once set, Simple get."**_
@@ -22,8 +23,8 @@ Supported **JSON**, **YAML**, **Hocon**, **TOML**, **SQL**, **MongoDB**... and m
## Features & Advantages ## Features & Advantages
Supported [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) and [SQL](impl/sql) based configuration files Supported [YAML](providers/yaml), [JSON](providers/json), [HOCON](providers/hocon) and [SQL](providers/sql)
format. based configuration files format.
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience - Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience
and efficiency. and efficiency.
@@ -37,12 +38,12 @@ For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/configure
For a detailed development guide, [CLICK HERE](.doc/README.md). For a detailed development guide, [CLICK HERE](.doc/README.md).
### Code Samples ### Preview
To quickly demonstrate the applicability of the project, here are a few practical demonstrations: 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) - [Database configuration.](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
- [Demonstration of all types of configuration instance classes.](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.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). 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). For more examples, see the [Development Guide](.doc/README.md).
+3 -2
View File
@@ -6,13 +6,14 @@
[![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml) [![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured) [![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured) ![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured)
![](https://visitor-badge.glitch.me/badge?page_id=configured.readme)
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ] README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
</div> </div>
# configured (config-framework) # configured _(配置文件框架)_
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
**一次配置,轻松读取!** **一次配置,轻松读取!**
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.1.0</version> <version>4.1.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -4,4 +4,5 @@ package cc.carm.lib.configuration;
* The root interface of the configuration file interfaces, * The root interface of the configuration file interfaces,
* which is used to label a class as a configuration. * which is used to label a class as a configuration.
*/ */
public interface Configuration { } public interface Configuration {
}
@@ -15,6 +15,7 @@ public class ValueAdapter<TYPE>
implements ValueSerializer<TYPE>, ValueParser<TYPE> { implements ValueSerializer<TYPE>, ValueParser<TYPE> {
protected final @NotNull ValueType<TYPE> type; protected final @NotNull ValueType<TYPE> type;
protected @Nullable ValueSerializer<TYPE> serializer; protected @Nullable ValueSerializer<TYPE> serializer;
protected @Nullable ValueParser<TYPE> deserializer; protected @Nullable ValueParser<TYPE> deserializer;
@@ -53,13 +54,21 @@ public class ValueAdapter<TYPE>
} }
@Override @Override
public Object serialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull TYPE value) throws Exception { 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"); if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
return serializer.serialize(holder, type, value); return serializer.serialize(holder, type, value);
} }
@Override @Override
public TYPE parse(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull Object value) throws Exception { 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"); if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
return deserializer.parse(holder, type, value); return deserializer.parse(holder, type, value);
} }
@@ -2,13 +2,13 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.function.DataFunction; import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.Contract; import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Arrays; import java.lang.reflect.ParameterizedType;
import java.util.HashSet; import java.lang.reflect.Type;
import java.util.Set; import java.util.*;
public class ValueAdapterRegistry { public class ValueAdapterRegistry {
@@ -61,7 +61,9 @@ public class ValueAdapterRegistry {
} }
} }
public <T> void register(@NotNull ValueType<T> type, @Nullable ValueSerializer<T> serializer, @Nullable ValueParser<T> deserializer) { public <T> void register(@NotNull ValueType<T> type,
@Nullable ValueSerializer<T> serializer,
@Nullable ValueParser<T> deserializer) {
if (serializer == null && deserializer == null) return; if (serializer == null && deserializer == null) return;
ValueAdapter<T> existing = adapterOf(type); ValueAdapter<T> existing = adapterOf(type);
if (existing != null) { if (existing != null) {
@@ -82,7 +84,9 @@ public class ValueAdapterRegistry {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) { public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) {
ValueAdapter<?> matched = adapters.stream().filter(adapter -> adapter.type().equals(type)).findFirst().orElse(null); ValueAdapter<?> matched = adapters.stream()
.filter(adapter -> adapter.type().equals(type))
.findFirst().orElse(null);
if (matched != null) return (ValueAdapter<T>) matched; if (matched != null) return (ValueAdapter<T>) matched;
// If no adapter found, try to find the adapter for the super type // If no adapter found, try to find the adapter for the super type
@@ -99,28 +103,112 @@ public class ValueAdapterRegistry {
return adapterOf(ValueType.of(type)); return adapterOf(ValueType.of(type));
} }
@Contract("_,_,null -> null")
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception { public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
return deserialize(holder, ValueType.of(type), source); return deserialize(holder, ValueType.of(type), source);
} }
@Contract("_,_,null -> null")
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception { public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception {
if (source == null) return null; // Null check if (source == null) return null; // Null check
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize if (!(type.getType() instanceof ParameterizedType) && type.isInstance(source)) {
ValueAdapter<T> adapter = adapterOf(type); return type.cast(source); // Not required to deserialize
if (adapter == null) throw new RuntimeException("No adapter for type " + type);
return adapter.parse(holder, type, source);
} }
@Contract("_,null -> null") ValueAdapter<T> adapter = adapterOf(type); // Try to find an existed adapter for the type
if (adapter != null) {
return adapter.parse(holder, type, source);
} // If no adapter found, we will try to handle the type manually
if (type.getRawType().isArray()) { // For arrays.
List<?> list = deserializeList(holder, type, source);
Object[] array = (Object[]) java.lang.reflect.Array.newInstance(type.getRawType().getComponentType(), list.size());
for (int i = 0; i < list.size(); i++) {
array[i] = deserialize(holder, type.getRawType().getComponentType(), list.get(i));
}
return type.cast(array);
} else if (type.getType() instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type.getType();
Type rawType = pt.getRawType();
Type[] typeArgs = pt.getActualTypeArguments();
if (rawType == List.class || rawType == Collection.class || rawType == ArrayList.class) {
return type.cast(new ArrayList<>(deserializeList(holder, ValueType.of(typeArgs[0]), source)));
} else if (rawType == Set.class || rawType == HashSet.class) {
return type.cast(new HashSet<>(deserializeList(holder, ValueType.of(typeArgs[0]), source)));
} else if (rawType == Map.class || rawType == LinkedHashMap.class) {
Map<?, ?> map;
if (source instanceof Map<?, ?>) {
map = (Map<?, ?>) source;
} else if (source instanceof ConfigureSection) {
map = ((ConfigureSection) source).asMap();
} else {
throw new IllegalArgumentException("Cannot deserialize to Map from " + source.getClass());
}
Map<Object, Object> resultMap = new LinkedHashMap<>(map.size());
for (Map.Entry<?, ?> entry : map.entrySet()) {
Object key = deserialize(holder, ValueType.of(typeArgs[0]), entry.getKey());
Object value = deserialize(holder, ValueType.of(typeArgs[1]), entry.getValue());
resultMap.put(key, value);
}
return type.cast(resultMap);
}
}
throw new RuntimeException("No adapter for type " + type);
}
@Nullable
public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception { public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception {
if (value == null) return null; // Null check if (value == null) return null; // Null check
ValueType<T> type = ValueType.of(value); ValueType<T> type = ValueType.of(value);
ValueAdapter<T> adapter = adapterOf(type); ValueAdapter<T> adapter = adapterOf(type);
if (adapter == null) return value; // No adapters, try to return the original value if (adapter != null) return adapter.serialize(holder, type, value);
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();
}
}
} }
@@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Value deserializer, convert base data to target value. * Value deserializer, convert base data to target value.
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
@FunctionalInterface @FunctionalInterface
public interface ValueParser<TYPE> { public interface ValueParser<TYPE> {
TYPE parse( @Nullable TYPE parse(
@NotNull ConfigurationHolder<?> holder, @NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull Object data @NotNull ValueType<? super TYPE> type, @NotNull Object data
) throws Exception; ) throws Exception;
@@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Value serializer, convert target value to base data. * Value serializer, convert target value to base data.
@@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
@FunctionalInterface @FunctionalInterface
public interface ValueSerializer<TYPE> { public interface ValueSerializer<TYPE> {
Object serialize( @Nullable Object serialize(
@NotNull ConfigurationHolder<?> holder, @NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value @NotNull ValueType<? super TYPE> type, @NotNull TYPE value
) throws Exception; ) throws Exception;
@@ -1,5 +1,6 @@
package cc.carm.lib.configuration.adapter; package cc.carm.lib.configuration.adapter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@@ -9,7 +10,13 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* Used to get the generic type. * {@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 abstract class ValueType<T> {
@@ -44,7 +51,7 @@ public abstract class ValueType<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> ValueType<T> of(final Type type) { public static <T> ValueType<T> of(final Type type) {
if (type == null) throw new NullPointerException("Type cannot be null"); if (type == null) throw new NullPointerException("Type cannot be null");
if (type instanceof Class<?>) { // Try handle primitive types if (type instanceof Class<?>) { // Try to fast handle primitive types
Class<?> clazz = (Class<?>) type; Class<?> clazz = (Class<?>) type;
for (ValueType<?> valueType : PRIMITIVE_TYPES) { for (ValueType<?> valueType : PRIMITIVE_TYPES) {
if (valueType.getRawType() == clazz) { if (valueType.getRawType() == clazz) {
@@ -56,10 +63,26 @@ public abstract class ValueType<T> {
}; };
} }
public static <T> ValueType<T> of(final Class<T> clazz) { public static <T> ValueType<T> of(final @NotNull Class<T> clazz) {
return of((Type) 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. * Get the generic type of the complex type.
* *
@@ -88,6 +111,7 @@ public abstract class ValueType<T> {
return of(parameterizedType); return of(parameterizedType);
} }
@ApiStatus.Internal
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) { private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
return new ValueType<T>(clazz) { return new ValueType<T>(clazz) {
}; };
@@ -107,40 +131,65 @@ public abstract class ValueType<T> {
return type; 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) { public boolean isSubtypeOf(Class<?> target) {
Class<?> rawType = getRawType(); Class<?> rawType = getRawType();
return target.isAssignableFrom(rawType); 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) { public boolean isSubtypeOf(ValueType<?> target) {
return target.isSubtypeOf(getRawType()); 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) { public boolean isInstance(Object obj) {
return obj != null && getRawType().isInstance(obj); return obj != null && getRawType().isInstance(obj);
} }
/** /**
* 提取当前 ValueType 的原始类型(Class 对象)。 * Extracts the raw type from the generic type.
* *
* @return 对应的 Class 对象 * @return The raw type of the generic type
* @throws IllegalStateException 如果无法提取出原始类型 * @throws IllegalStateException if the type is not a Class or ParameterizedType
*/ */
public Class<?> getRawType() { @SuppressWarnings("unchecked")
public Class<T> getRawType() {
if (type instanceof Class<?>) { if (type instanceof Class<?>) {
return (Class<?>) type; return (Class<T>) type;
} }
if (type instanceof ParameterizedType) { if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type; ParameterizedType pt = (ParameterizedType) type;
Type raw = pt.getRawType(); Type raw = pt.getRawType();
if (raw instanceof Class<?>) { if (raw instanceof Class<?>) {
return (Class<?>) raw; return (Class<T>) raw;
} }
} }
throw new IllegalStateException("Unsupported type: " + type); 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") @SuppressWarnings("unchecked")
public T cast(Object obj) { public T cast(Object obj) {
if (!isInstance(obj)) { if (!isInstance(obj)) {
@@ -149,6 +198,12 @@ public abstract class ValueType<T> {
return (T) obj; 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 @Override
public String toString() { public String toString() {
if (type instanceof Class<?>) { if (type instanceof Class<?>) {
@@ -5,6 +5,8 @@ import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.section.ConfigureSection; import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*; import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*;
public interface StandardAdapters { public interface StandardAdapters {
@@ -17,6 +19,12 @@ public interface StandardAdapters {
@NotNull ValueAdapter<Enum<?>> ENUMS = PrimitiveAdapter.ofEnum(); @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<>( @NotNull ValueAdapter<ConfigureSection> SECTIONS = new ValueAdapter<>(
ValueType.of(ConfigureSection.class), ValueType.of(ConfigureSection.class),
(provider, type, value) -> value, (provider, type, value) -> value,
@@ -1,6 +1,8 @@
package cc.carm.lib.configuration.builder; package cc.carm.lib.configuration.builder;
import cc.carm.lib.configuration.adapter.ValueType; 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.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
@@ -11,11 +13,12 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
public abstract class AbstractConfigBuilder< public abstract class AbstractConfigBuilder<
TYPE, RESULT extends ConfigValue<TYPE>, HOLDER extends ConfigurationHolder<?>, TYPE, UNIT, RESULT extends ConfigValue<TYPE, UNIT>, HOLDER extends ConfigurationHolder<?>,
SELF extends AbstractConfigBuilder<TYPE, RESULT, HOLDER, SELF> SELF extends AbstractConfigBuilder<TYPE, UNIT, RESULT, HOLDER, SELF>
> { > {
protected final Class<? super HOLDER> providerClass; protected final Class<? super HOLDER> providerClass;
@@ -24,6 +27,7 @@ public abstract class AbstractConfigBuilder<
protected @Nullable HOLDER holder; protected @Nullable HOLDER holder;
protected @Nullable String path; protected @Nullable String path;
protected @NotNull ValueValidator<UNIT> valueValidator = ValueValidator.none();
protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null; protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null;
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> { protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> {
}; };
@@ -37,54 +41,121 @@ public abstract class AbstractConfigBuilder<
return type; return type;
} }
protected abstract @NotNull SELF self(); protected abstract SELF self();
public abstract @NotNull RESULT build(); public abstract @NotNull RESULT build();
public @NotNull SELF holder(@Nullable HOLDER holder) { public SELF holder(@Nullable HOLDER holder) {
this.holder = holder; this.holder = holder;
return self(); return self();
} }
public @NotNull SELF path(@Nullable String path) { public SELF path(@Nullable String path) {
this.path = path; this.path = path;
return self(); return self();
} }
public @NotNull SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) { /**
* 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; this.initializer = initializer;
return self(); return self();
} }
public @NotNull SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) { public SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
return initializer(initializer.andThen(initializer)); return initializer(initializer.andThen(initializer));
} }
public @NotNull SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) { public SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
return append((provider, valuePath) -> initializer.accept(provider)); return append((provider, valuePath) -> initializer.accept(provider));
} }
public @NotNull SELF defaults(@Nullable TYPE defaultValue) { public SELF defaults(@Nullable TYPE defaultValue) {
return defaults(() -> defaultValue); return defaults(() -> defaultValue);
} }
public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) { public SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
this.defaultValueSupplier = supplier; this.defaultValueSupplier = supplier;
return self(); return self();
} }
public <M> @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) { public SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
return append((h, p) -> metaConsumer.accept(h.metadata(p))); return append((h, p) -> metaConsumer.accept(h.metadata(p)));
} }
public <M> @NotNull SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) { public <M> SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
return meta(h -> h.set(type, value)); return meta(h -> h.set(type, value));
} }
protected @NotNull ValueManifest<TYPE> buildManifest() { protected @NotNull ValueManifest<TYPE, UNIT> buildManifest() {
return new ValueManifest<>( return new ValueManifest<>(
type(), this.defaultValueSupplier, this.initializer, type(), this.defaultValueSupplier, this.valueValidator,
this.holder, this.path this.initializer, this.holder, this.path
); );
} }
@@ -4,8 +4,11 @@ import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.ConfigValue;
public abstract class CommonConfigBuilder<TYPE, RESULT extends ConfigValue<TYPE>, SELF extends CommonConfigBuilder<TYPE, RESULT, SELF>> public abstract class CommonConfigBuilder<
extends AbstractConfigBuilder<TYPE, RESULT, ConfigurationHolder<?>, SELF> { 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) { protected CommonConfigBuilder(ValueType<TYPE> type) {
super(ConfigurationHolder.class, type); super(ConfigurationHolder.class, type);
@@ -4,7 +4,7 @@ import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.CommonConfigBuilder; import cc.carm.lib.configuration.builder.CommonConfigBuilder;
import cc.carm.lib.configuration.function.DataFunction; import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.function.ValueConsumer; import cc.carm.lib.configuration.function.ValueComposer;
import cc.carm.lib.configuration.function.ValueHandler; import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection; import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.ConfigValue;
@@ -14,45 +14,45 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public abstract class AbstractSectionBuilder< public abstract class AbstractSectionBuilder<
TYPE, PARAM, TYPE, UNIT,
RESULT extends ConfigValue<TYPE>, RESULT extends ConfigValue<TYPE, UNIT>,
SELF extends AbstractSectionBuilder<TYPE, PARAM, RESULT, SELF> SELF extends AbstractSectionBuilder<TYPE, UNIT, RESULT, SELF>
> extends CommonConfigBuilder<TYPE, RESULT, SELF> { > extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF> {
protected final @NotNull ValueType<PARAM> paramType; protected final @NotNull ValueType<UNIT> paramType;
protected @NotNull ValueHandler<ConfigureSection, PARAM> parser; protected @NotNull ValueHandler<ConfigureSection, UNIT> parser;
protected @NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer; protected @NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer;
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType, protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<UNIT> paramType,
@NotNull ValueHandler<ConfigureSection, PARAM> parser, @NotNull ValueHandler<ConfigureSection, UNIT> parser,
@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) { @NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
super(type); super(type);
this.paramType = paramType; this.paramType = paramType;
this.parser = parser; this.parser = parser;
this.serializer = serializer; this.serializer = serializer;
} }
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, PARAM> valueParser) { public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, UNIT> valueParser) {
return parse((p, section) -> valueParser.handle(section)); return parse((p, section) -> valueParser.handle(section));
} }
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, PARAM> valueParser) { public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, UNIT> valueParser) {
this.parser = valueParser; this.parser = valueParser;
return self(); return self();
} }
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) { public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
this.serializer = serializer; this.serializer = serializer;
return self(); return self();
} }
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, ? extends Map<String, Object>> serializer) { public @NotNull SELF serialize(@NotNull DataFunction<UNIT, ? extends Map<String, Object>> serializer) {
return serialize((p, value) -> serializer.handle(value)); return serialize((p, value) -> serializer.handle(value));
} }
public @NotNull SELF serialize(@NotNull ValueConsumer<Map<String, Object>, PARAM> serializer) { public @NotNull SELF serialize(@NotNull ValueComposer<Map<String, Object>, UNIT> serializer) {
return serialize((h, value) -> { return serialize((h, value) -> {
Map<String, Object> map = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
serializer.accept(h, map, value); serializer.accept(h, map, value);
@@ -60,7 +60,7 @@ public abstract class AbstractSectionBuilder<
}); });
} }
protected ValueAdapter<PARAM> buildAdapter() { protected ValueAdapter<UNIT> buildAdapter() {
return new ValueAdapter<>(this.paramType) return new ValueAdapter<>(this.paramType)
.parser((p, type, data) -> { .parser((p, type, data) -> {
ConfigureSection section = p.deserialize(ConfigureSection.class, data); ConfigureSection section = p.deserialize(ConfigureSection.class, data);
@@ -9,19 +9,19 @@ import cc.carm.lib.configuration.value.ConfigValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public abstract class AbstractSourceBuilder< public abstract class AbstractSourceBuilder<
V, SOURCE, PARAM, RESULT extends ConfigValue<V>, V, SOURCE, UNIT, RESULT extends ConfigValue<V, UNIT>,
SELF extends AbstractSourceBuilder<V, SOURCE, PARAM, RESULT, SELF> SELF extends AbstractSourceBuilder<V, SOURCE, UNIT, RESULT, SELF>
> extends CommonConfigBuilder<V, RESULT, SELF> { > extends CommonConfigBuilder<V, UNIT, RESULT, SELF> {
protected final @NotNull ValueType<SOURCE> sourceType; protected final @NotNull ValueType<SOURCE> sourceType;
protected final @NotNull ValueType<PARAM> paramType; protected final @NotNull ValueType<UNIT> paramType;
protected @NotNull ValueHandler<SOURCE, PARAM> valueParser; protected @NotNull ValueHandler<SOURCE, UNIT> valueParser;
protected @NotNull ValueHandler<PARAM, SOURCE> valueSerializer; protected @NotNull ValueHandler<UNIT, SOURCE> valueSerializer;
protected AbstractSourceBuilder(@NotNull ValueType<V> type, protected AbstractSourceBuilder(@NotNull ValueType<V> type,
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<PARAM> paramType, @NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<UNIT> paramType,
@NotNull ValueHandler<SOURCE, PARAM> parser, @NotNull ValueHandler<SOURCE, UNIT> parser,
@NotNull ValueHandler<PARAM, SOURCE> serializer) { @NotNull ValueHandler<UNIT, SOURCE> serializer) {
super(type); super(type);
this.sourceType = sourceType; this.sourceType = sourceType;
this.paramType = paramType; this.paramType = paramType;
@@ -29,25 +29,25 @@ public abstract class AbstractSourceBuilder<
this.valueSerializer = serializer; this.valueSerializer = serializer;
} }
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, PARAM> parser) { public @NotNull SELF parse(@NotNull DataFunction<SOURCE, UNIT> parser) {
return parse((p, source) -> parser.handle(source)); return parse((p, source) -> parser.handle(source));
} }
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, PARAM> parser) { public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, UNIT> parser) {
this.valueParser = parser; this.valueParser = parser;
return self(); return self();
} }
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, SOURCE> serializer) { public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, SOURCE> serializer) {
this.valueSerializer = serializer; this.valueSerializer = serializer;
return self(); return self();
} }
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, SOURCE> serializer) { public @NotNull SELF serialize(@NotNull DataFunction<UNIT, SOURCE> serializer) {
return serialize((p, value) -> serializer.handle(value)); return serialize((p, value) -> serializer.handle(value));
} }
protected ValueAdapter<PARAM> buildAdapter() { protected ValueAdapter<UNIT> buildAdapter() {
return new ValueAdapter<>(this.paramType) return new ValueAdapter<>(this.paramType)
.parser((holder, type, data) -> { .parser((holder, type, data) -> {
SOURCE source = holder.deserialize(this.sourceType, data); SOURCE source = holder.deserialize(this.sourceType, data);
@@ -8,9 +8,11 @@ import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, ConfiguredList<V>, SectionListBuilder<V>> { public class SectionListBuilder<V>
extends AbstractSectionBuilder<List<V>, V, ConfiguredList<V>, SectionListBuilder<V>> {
protected @NotNull Supplier<? extends List<V>> constructor; protected @NotNull Supplier<? extends List<V>> constructor;
@@ -32,6 +34,14 @@ public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, Co
return defaults(new ArrayList<>(values)); return defaults(new ArrayList<>(values));
} }
public final @NotNull SectionListBuilder<V> defaults(@NotNull Consumer<List<V>> constructor) {
return defaults(() -> {
List<V> list = new ArrayList<>();
constructor.accept(list);
return list;
});
}
public SectionListBuilder<V> constructor(@NotNull Supplier<? extends List<V>> constructor) { public SectionListBuilder<V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
this.constructor = constructor; this.constructor = constructor;
return this; return this;
@@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SourceListBuilder<SOURCE, V> public class SourceListBuilder<SOURCE, V>
@@ -34,6 +35,23 @@ public class SourceListBuilder<SOURCE, V>
return defaults(new ArrayList<>(values)); return defaults(new ArrayList<>(values));
} }
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull Consumer<List<V>> constructor) {
return defaults(() -> {
List<V> list = new ArrayList<>();
constructor.accept(list);
return list;
});
}
public SourceListBuilder<SOURCE, V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
this.constructor = constructor;
return this;
}
public <LIST extends List<V>> SourceListBuilder<SOURCE, V> construct(@NotNull LIST list) {
return constructor(() -> list);
}
@Override @Override
protected @NotNull SourceListBuilder<SOURCE, V> self() { protected @NotNull SourceListBuilder<SOURCE, V> self() {
return this; return this;
@@ -14,10 +14,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SectionMapBuilder<MAP extends Map<K, V>, K, V> public class SectionMapBuilder<MAP extends Map<K, V>, K, V>
extends AbstractSectionBuilder< extends AbstractSectionBuilder<Map<K, V>, V, ConfiguredMap<K, V>, SectionMapBuilder<MAP, K, V>> {
Map<K, V>, V, ConfiguredMap<K, V>,
SectionMapBuilder<MAP, K, V>
> {
protected final @NotNull ValueType<K> keyType; protected final @NotNull ValueType<K> keyType;
@@ -13,10 +13,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V> public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
extends AbstractSourceBuilder< extends AbstractSourceBuilder<Map<K, V>, SOURCE, V, ConfiguredMap<K, V>, SourceMapBuilder<MAP, SOURCE, K, V>> {
Map<K, V>, SOURCE, V, ConfiguredMap<K, V>,
SourceMapBuilder<MAP, SOURCE, K, V>
> {
protected final @NotNull ValueType<K> keyType; protected final @NotNull ValueType<K> keyType;
@@ -1,7 +1,6 @@
package cc.carm.lib.configuration.builder.value; package cc.carm.lib.configuration.builder.value;
import cc.carm.lib.configuration.adapter.ValueType; 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.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection; import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -6,7 +6,8 @@ import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class SourceValueBuilder<S, V> extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> { 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, public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
@@ -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,17 @@
package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface DataValidator<T> {
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,29 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ValueComposer<T, U> {
/**
* 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);
};
}
}
@@ -1,21 +0,0 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ValueConsumer<U, T> {
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull U unit, @NotNull T data) throws Exception;
default ValueConsumer<U, T> andThen(ValueConsumer<? super U, ? super T> after) {
return (holder, unit, data) -> {
accept(holder, unit, data);
after.accept(holder, unit, data);
};
}
}
@@ -0,0 +1,47 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface ValueValidator<T> {
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);
}
};
}
}
@@ -2,7 +2,9 @@ package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.adapter.*; import cc.carm.lib.configuration.adapter.*;
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters; 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.DataFunction;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.loader.PathGenerator; import cc.carm.lib.configuration.source.loader.PathGenerator;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
@@ -33,15 +35,17 @@ public abstract class ConfigurationFactory<
SELF SELF
> { > {
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry(); protected @NotNull ValueAdapterRegistry adapters = new ValueAdapterRegistry();
protected ConfigurationOptionHolder options = new ConfigurationOptionHolder(); protected @NotNull ConfigurationOptionHolder options = new ConfigurationOptionHolder();
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>(); protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
protected ConfigurationInitializer initializer = new ConfigurationInitializer(); protected @NotNull ConfigurationInitializer initializer = new ConfigurationInitializer();
protected @NotNull ConfigExceptionHandler exceptionHandler = ConfigExceptionHandler.print();
public ConfigurationFactory() { protected ConfigurationFactory() {
this.adapters.register(StandardAdapters.PRIMITIVES); this.adapters.register(StandardAdapters.PRIMITIVES);
this.adapters.register(StandardAdapters.SECTIONS); this.adapters.register(StandardAdapters.SECTIONS);
this.adapters.register(StandardAdapters.ENUMS); this.adapters.register(StandardAdapters.ENUMS);
this.adapters.register(StandardAdapters.UUID);
} }
protected abstract SELF self(); protected abstract SELF self();
@@ -146,6 +150,11 @@ public abstract class ConfigurationFactory<
return self(); return self();
} }
public SELF exceptionally(@NotNull ConfigExceptionHandler handler) {
this.exceptionHandler = handler;
return self();
}
/** /**
* Supply the base path generator for this configuration holder * Supply the base path generator for this configuration holder
* *
@@ -153,9 +162,7 @@ public abstract class ConfigurationFactory<
* @return this * @return this
*/ */
public SELF pathGenerator(PathGenerator generator) { public SELF pathGenerator(PathGenerator generator) {
return initializer(loader -> { return initializer(loader -> loader.pathGenerator(generator));
loader.pathGenerator(generator);
});
} }
/** /**
@@ -174,6 +181,19 @@ public abstract class ConfigurationFactory<
return initializer(loader -> loader.registerAnnotation(annotation, metadata, 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. * Build the configuration holder.
* *
@@ -3,6 +3,7 @@ package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
import cc.carm.lib.configuration.adapter.ValueType; 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.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
@@ -12,7 +13,6 @@ import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
import cc.carm.lib.configuration.source.section.ConfigureSource; import cc.carm.lib.configuration.source.section.ConfigureSource;
import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.ValueManifest;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView; import org.jetbrains.annotations.UnmodifiableView;
@@ -31,14 +31,25 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
protected final @NotNull ConfigurationInitializer initializer; protected final @NotNull ConfigurationInitializer initializer;
protected @NotNull ConfigExceptionHandler exceptionHandler;
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters, public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options, @NotNull ConfigurationOptionHolder options,
@NotNull Map<String, ConfigurationMetaHolder> metadata, @NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer) { @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.initializer = initializer;
this.adapters = adapters; this.adapters = adapters;
this.options = options; this.options = options;
this.metadata = metadata; this.metadata = metadata;
this.exceptionHandler = exceptionHandler;
} }
public abstract @NotNull SOURCE config(); public abstract @NotNull SOURCE config();
@@ -75,7 +86,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
@NotNull @NotNull
@UnmodifiableView @UnmodifiableView
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<M> filter) { public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<@Nullable M> filter) {
Map<String, M> metas = new LinkedHashMap<>(); Map<String, M> metas = new LinkedHashMap<>();
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) { for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
M data = entry.getValue().get(type); M data = entry.getValue().get(type);
@@ -86,7 +97,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
@NotNull @NotNull
@UnmodifiableView @UnmodifiableView
public Map<String, ConfigValue<?>> registeredValues() { public Map<String, ConfigValue<?, ?>> registeredValues() {
return extractMetadata(StandardMeta.VALUE); return extractMetadata(StandardMeta.VALUE);
} }
@@ -98,17 +109,17 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
return initializer; return initializer;
} }
@Contract("_,null -> null") @Nullable
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception { public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
return adapters().deserialize(this, type, source); return adapters().deserialize(this, type, source);
} }
@Contract("_,null -> null") @Nullable
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception { public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
return adapters().deserialize(this, type, source); return adapters().deserialize(this, type, source);
} }
@Contract("null -> null") @Nullable
public <T> Object serialize(@Nullable T value) throws Exception { public <T> Object serialize(@Nullable T value) throws Exception {
return adapters().serialize(this, value); return adapters().serialize(this, value);
} }
@@ -117,7 +128,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try { try {
initializer.initialize(this, configClass); initializer.initialize(this, configClass);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(configClass.getName(), e);
} }
} }
@@ -125,12 +136,20 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try { try {
initializer.initialize(this, config); initializer.initialize(this, config);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(config.getClass().getName(), e);
} }
} }
public void initialize(@NotNull ValueManifest<?> value) { public void initialize(@NotNull ValueManifest<?, ?> value) {
value.holder(this); 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;
}
} }
@@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
public interface ConfigInitializeHandler<T, V> { public interface ConfigInitializeHandler<T, V> {
static <T, V> ConfigInitializeHandler<T, V> start() { static <T, V> ConfigInitializeHandler<T, V> start() {
return (provider, path, value, instace) -> { return (provider, path, value, instance) -> {
}; };
} }
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.source.loader; package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.Configuration; 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.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
import cc.carm.lib.configuration.source.meta.StandardMeta; import cc.carm.lib.configuration.source.meta.StandardMeta;
@@ -21,7 +22,7 @@ import java.util.function.Function;
public class ConfigurationInitializer { public class ConfigurationInitializer {
protected @NotNull PathGenerator pathGenerator; protected @NotNull PathGenerator pathGenerator;
protected @NotNull ConfigInitializeHandler<Field, ConfigValue<?>> valueInitializer; protected @NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer;
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer; protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer;
public ConfigurationInitializer() { public ConfigurationInitializer() {
@@ -29,7 +30,7 @@ public class ConfigurationInitializer {
} }
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator, public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> valueInitializer, @NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer,
@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) { @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
this.pathGenerator = pathGenerator; this.pathGenerator = pathGenerator;
this.valueInitializer = valueInitializer; this.valueInitializer = valueInitializer;
@@ -44,11 +45,11 @@ public class ConfigurationInitializer {
return pathGenerator; return pathGenerator;
} }
public ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer() { public ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer() {
return valueInitializer; return valueInitializer;
} }
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer) { public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
this.valueInitializer = fieldInitializer; this.valueInitializer = fieldInitializer;
} }
@@ -60,7 +61,7 @@ public class ConfigurationInitializer {
this.classInitializer = classInitializer; this.classInitializer = classInitializer;
} }
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer) { public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
this.valueInitializer = this.valueInitializer.andThen(fieldInitializer); this.valueInitializer = this.valueInitializer.andThen(fieldInitializer);
} }
@@ -95,6 +96,14 @@ public class ConfigurationInitializer {
registerFieldAnnotation(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) { public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
return pathGenerator.getFieldPath(holder, parentPath, field); return pathGenerator.getFieldPath(holder, parentPath, field);
@@ -125,7 +134,7 @@ public class ConfigurationInitializer {
try { try {
this.classInitializer.whenInitialize(holder, path, root.getClass(), root); this.classInitializer.whenInitialize(holder, path, root.getClass(), root);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); holder.throwing(path, e);
} }
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path)); Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
} }
@@ -142,7 +151,7 @@ public class ConfigurationInitializer {
try { try {
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField); this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); holder.throwing(path, e);
} }
for (Field field : clazz.getDeclaredFields()) { for (Field field : clazz.getDeclaredFields()) {
@@ -163,9 +172,9 @@ public class ConfigurationInitializer {
field.setAccessible(true); field.setAccessible(true);
Object object = field.get(source); Object object = field.get(source);
// //
if (object instanceof ConfigValue<?>) { if (object instanceof ConfigValue<?, ?>) {
// 目标是 ConfigValue 实例,进行具体的初始化注入 // 目标是 ConfigValue 实例,进行具体的初始化注入
ConfigValue<?> value = (ConfigValue<?>) object; ConfigValue<?, ?> value = (ConfigValue<?, ?>) object;
String path = getFieldPath(holder, parent, field); String path = getFieldPath(holder, parent, field);
if (path == null) return; if (path == null) return;
value.initialize(holder, path); value.initialize(holder, path);
@@ -176,7 +185,7 @@ public class ConfigurationInitializer {
try { try {
this.valueInitializer.whenInitialize(holder, path, field, value); this.valueInitializer.whenInitialize(holder, path, field, value);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); holder.throwing(path, e);
} }
if (holder.option(StandardOptions.PRELOAD)) { if (holder.option(StandardOptions.PRELOAD)) {
value.get(); // Preload the value by calling #get method. value.get(); // Preload the value by calling #get method.
@@ -7,6 +7,6 @@ public interface StandardMeta {
/** /**
* To mark the {@link ConfigValue} instance of specific path. * To mark the {@link ConfigValue} instance of specific path.
*/ */
ConfigurationMetadata<ConfigValue<?>> VALUE = ConfigurationMetadata.of(); ConfigurationMetadata<ConfigValue<?, ?>> VALUE = ConfigurationMetadata.of();
} }
@@ -477,7 +477,7 @@ public interface ConfigureSection {
* @return The boolean if the path exists and is a boolean, otherwise false. * @return The boolean if the path exists and is a boolean, otherwise false.
*/ */
default boolean getBoolean(@NotNull String path) { default boolean getBoolean(@NotNull String path) {
return getBoolean(path, false); return getBoolean(path, null);
} }
/** /**
@@ -509,7 +509,7 @@ public interface ConfigureSection {
* @return The byte if the path exists and is a byte, otherwise 0. * @return The byte if the path exists and is a byte, otherwise 0.
*/ */
default @Nullable Byte getByte(@NotNull String path) { default @Nullable Byte getByte(@NotNull String path) {
return getByte(path, (byte) 0); return getByte(path, null);
} }
/** /**
@@ -541,7 +541,7 @@ public interface ConfigureSection {
* @return The short if the path exists and is a short, otherwise 0. * @return The short if the path exists and is a short, otherwise 0.
*/ */
default @Nullable Short getShort(@NotNull String path) { default @Nullable Short getShort(@NotNull String path) {
return getShort(path, (short) 0); return getShort(path, null);
} }
/** /**
@@ -573,7 +573,7 @@ public interface ConfigureSection {
* @return The int if the path exists and is an int, otherwise 0. * @return The int if the path exists and is an int, otherwise 0.
*/ */
default @Nullable Integer getInt(@NotNull String path) { default @Nullable Integer getInt(@NotNull String path) {
return getInt(path, 0); return getInt(path, null);
} }
/** /**
@@ -606,7 +606,7 @@ public interface ConfigureSection {
* @return The long if the path exists and is a long, otherwise 0. * @return The long if the path exists and is a long, otherwise 0.
*/ */
default @Nullable Long getLong(@NotNull String path) { default @Nullable Long getLong(@NotNull String path) {
return getLong(path, 0L); return getLong(path, null);
} }
/** /**
@@ -638,7 +638,7 @@ public interface ConfigureSection {
* @return The float if the path exists and is a float, otherwise 0. * @return The float if the path exists and is a float, otherwise 0.
*/ */
default @Nullable Float getFloat(@NotNull String path) { default @Nullable Float getFloat(@NotNull String path) {
return getFloat(path, 0.0F); return getFloat(path, null);
} }
/** /**
@@ -670,7 +670,7 @@ public interface ConfigureSection {
* @return The double if the path exists and is a double, otherwise 0. * @return The double if the path exists and is a double, otherwise 0.
*/ */
default @Nullable Double getDouble(@NotNull String path) { default @Nullable Double getDouble(@NotNull String path) {
return getDouble(path, 0.0D); return getDouble(path, null);
} }
/** /**
@@ -692,7 +692,7 @@ public interface ConfigureSection {
* @return True if the value is present and is a char, false otherwise. * @return True if the value is present and is a char, false otherwise.
*/ */
default boolean isChar(@NotNull String path) { default boolean isChar(@NotNull String path) {
return isType(path, Boolean.class); return isType(path, Character.class);
} }
/** /**
@@ -750,7 +750,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of values from the section * Get a list of values from current section
* <p> * <p>
* If the path does not exist, an empty list will be returned * If the path does not exist, an empty list will be returned
* <br>Any changes please use {@link #set(String, Object)} after changes * <br>Any changes please use {@link #set(String, Object)} after changes
@@ -765,7 +765,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of strings from the section * Get a list of strings from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -776,7 +776,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of integer from the section * Get a list of integer from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -787,7 +787,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of long from the section * Get a list of long from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -798,7 +798,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of double from the section * Get a list of double from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -809,7 +809,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of floats from the section * Get a list of floats from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -820,7 +820,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of bytes from the section * Get a list of bytes from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -831,7 +831,7 @@ public interface ConfigureSection {
} }
/** /**
* Get a list of char from the section * Get a list of char from current section
* <p> Limitations see {@link #getList(String, DataFunction)} * <p> Limitations see {@link #getList(String, DataFunction)}
* *
* @param path The path to get the list from * @param path The path to get the list from
@@ -842,7 +842,22 @@ public interface ConfigureSection {
} }
/** /**
* Get the specific type of collection from the section. * 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;
}
return null;
});
}
/**
* Get the specific type of collection from current section.
* *
* @param path The path to get the collection from * @param path The path to get the collection from
* @param constructor The constructor of the collection * @param constructor The constructor of the collection
@@ -858,7 +873,7 @@ public interface ConfigureSection {
} }
/** /**
* Get the specific type of steam from the section. * Get the specific type of steam from current section.
* *
* @param path The path to get the stream from * @param path The path to get the stream from
* @return The stream of values * @return The stream of values
@@ -869,7 +884,7 @@ public interface ConfigureSection {
} }
/** /**
* Get the specific type of steam from the section. * Get the specific type of steam from current section.
* *
* @param path The path to get the stream from * @param path The path to get the stream from
* @param parser The function to parse the values * @param parser The function to parse the values
@@ -33,9 +33,9 @@ import java.util.Optional;
* @see ValueManifest Base class providing metadata and default value handling * @see ValueManifest Base class providing metadata and default value handling
* @see ConfigurationHolder Responsible for configuration source persistence * @see ConfigurationHolder Responsible for configuration source persistence
*/ */
public abstract class ConfigValue<T> extends ValueManifest<T> { public abstract class ConfigValue<T, U> extends ValueManifest<T, U> {
protected ConfigValue(@NotNull ValueManifest<T> manifest) { protected ConfigValue(@NotNull ValueManifest<T, U> manifest) {
super(manifest); super(manifest);
} }
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.value; package cc.carm.lib.configuration.value;
import cc.carm.lib.configuration.adapter.ValueType; 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.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.section.ConfigureSource; import cc.carm.lib.configuration.source.section.ConfigureSource;
@@ -11,37 +12,48 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ValueManifest<T> { public class ValueManifest<TYPE, UNIT> {
protected final @NotNull ValueType<T> type; protected final @NotNull ValueType<TYPE> type;
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer; protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
protected @Nullable ConfigurationHolder<?> holder; protected @Nullable ConfigurationHolder<?> holder;
protected @Nullable String path; // Section path protected @Nullable String path; // Section path
protected @NotNull Supplier<@Nullable T> defaultSupplier; protected @NotNull ValueValidator<UNIT> validator;
protected @NotNull Supplier<@Nullable TYPE> defaultSupplier;
public ValueManifest(@NotNull ValueType<T> type) { public ValueManifest(@NotNull ValueType<TYPE> type) {
this(type, () -> null, EMPTY_INITIALIZER, null, null); this(type, () -> null, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
} }
public ValueManifest(@NotNull T defaultValue) { public ValueManifest(@NotNull TYPE defaultValue) {
this(ValueType.of(defaultValue), () -> defaultValue); this(ValueType.of(defaultValue), () -> defaultValue);
} }
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier) { public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier) {
this(type, defaultSupplier, EMPTY_INITIALIZER, null, null); this(type, defaultSupplier, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
} }
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier, 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) { @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
this(type, defaultSupplier, initializer, null, null); this(type, defaultSupplier, validator, initializer, null, null);
} }
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier, public ValueManifest(@NotNull ValueType<TYPE> type,
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<UNIT> validator,
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer, @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
@Nullable ConfigurationHolder<?> holder, @Nullable String path) { @Nullable ConfigurationHolder<?> holder, @Nullable String path) {
this.type = type; this.type = type;
this.validator = validator;
this.initializer = initializer; this.initializer = initializer;
this.defaultSupplier = defaultSupplier; this.defaultSupplier = defaultSupplier;
this.holder = holder; this.holder = holder;
@@ -49,8 +61,8 @@ public class ValueManifest<T> {
initialize(); initialize();
} }
protected ValueManifest(@NotNull ValueManifest<T> manifest) { protected ValueManifest(@NotNull ValueManifest<TYPE, UNIT> manifest) {
this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path); this(manifest.type, manifest.defaultSupplier, manifest.validator, manifest.initializer, manifest.holder, manifest.path);
} }
public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) { public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
@@ -63,7 +75,7 @@ public class ValueManifest<T> {
if (holder != null && path != null) this.initializer.accept(holder, path); if (holder != null && path != null) this.initializer.accept(holder, path);
} }
public @NotNull ValueType<T> type() { public @NotNull ValueType<TYPE> type() {
return this.type; return this.type;
} }
@@ -75,20 +87,37 @@ public class ValueManifest<T> {
this.path = path; this.path = path;
} }
public @Nullable T defaults() { public @Nullable TYPE defaults() {
return this.defaultSupplier.get(); return this.defaultSupplier.get();
} }
public void defaults(@Nullable T defaultValue) { public void defaults(@Nullable TYPE defaultValue) {
defaults(() -> defaultValue); defaults(() -> defaultValue);
} }
public void defaults(@NotNull Supplier<@Nullable T> defaultValue) { public void defaults(@NotNull Supplier<@Nullable TYPE> defaultValue) {
this.defaultSupplier = defaultValue; this.defaultSupplier = defaultValue;
} }
public boolean hasDefaults() { public boolean hasDefaults() {
return this.defaultSupplier.get() != null; 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() { public @NotNull String path() {
@@ -105,7 +134,7 @@ public class ValueManifest<T> {
return holder().config(); return holder().config();
} }
public ConfigurationMetaHolder metadata() { public @NotNull ConfigurationMetaHolder metadata() {
return holder().metadata(path()); return holder().metadata(path());
} }
@@ -119,6 +148,15 @@ public class ValueManifest<T> {
config().set(path(), 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) -> { private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
}; };
@@ -9,12 +9,12 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public abstract class CachedConfigValue<T> extends ConfigValue<T> { public abstract class CachedConfigValue<T, U> extends ConfigValue<T, U> {
protected @Nullable T cachedValue; protected @Nullable T cachedValue;
protected long parsedTime = -1; protected long parsedTime = -1;
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) { protected CachedConfigValue(@NotNull ValueManifest<T, U> manifest) {
super(manifest); super(manifest);
} }
@@ -5,6 +5,7 @@ import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer; import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.list.ConfigListBuilder; import cc.carm.lib.configuration.builder.list.ConfigListBuilder;
import cc.carm.lib.configuration.builder.list.SourceListBuilder;
import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CachedConfigValue; import cc.carm.lib.configuration.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -15,7 +16,7 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> { public class ConfiguredList<V> extends CachedConfigValue<List<V>, V> implements List<V> {
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull Class<T> type) { public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull Class<T> type) {
return builderOf(ValueType.of(type)); return builderOf(ValueType.of(type));
@@ -25,10 +26,26 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
return new ConfigListBuilder<>(type); return new ConfigListBuilder<>(type);
} }
public static <T> @NotNull SourceListBuilder<T, T> with(@NotNull Class<T> registeredType) {
return with(ValueType.of(registeredType));
}
public static <T> @NotNull SourceListBuilder<T, T> with(@NotNull ValueType<T> registeredType) {
return new ConfigListBuilder<>(registeredType).from(registeredType);
}
@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();
}
protected final @NotNull Supplier<? extends List<V>> constructor; protected final @NotNull Supplier<? extends List<V>> constructor;
protected final @NotNull ValueAdapter<V> paramAdapter; protected final @NotNull ValueAdapter<V> paramAdapter;
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest, public ConfiguredList(@NotNull ValueManifest<List<V>, V> manifest,
@NotNull Supplier<? extends List<V>> constructor, @NotNull Supplier<? extends List<V>> constructor,
@NotNull ValueAdapter<V> paramAdapter) { @NotNull ValueAdapter<V> paramAdapter) {
super(manifest); super(manifest);
@@ -70,40 +87,46 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
if (!cacheExpired()) return getCachedOrDefault(createList()); if (!cacheExpired()) return getCachedOrDefault(createList());
// Data that is outdated and needs to be parsed again. // Data that is outdated and needs to be parsed again.
List<V> list = createList(); List<V> list = createList();
try {
List<?> data = config().contains(path()) ? config().getList(path()) : null; List<?> data = config().contains(path()) ? config().getList(path()) : null;
if (data == null) return getDefaultFirst(list); if (data == null) return getDefaultFirst(list);
ValueParser<V> parser = parser(); ValueParser<V> parser = parser();
if (parser == null) return getDefaultFirst(list); if (parser == null) return getDefaultFirst(list);
int i = 0;
for (Object dataVal : data) { for (Object dataVal : data) {
if (dataVal == null) continue; if (dataVal == null) continue;
try { try {
list.add(parser.parse(holder(), paramType(), dataVal)); list.add(withValidated(parser.parse(holder(), paramType(), dataVal)));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(path + "[" + i + "]", e);
} }
} }
} catch (Exception ex) {
throwing(ex);
}
return updateCache(list); return updateCache(list);
} }
@Override @Override
public void set(@Nullable List<V> value) { public void set(@Nullable List<V> list) {
updateCache(value); updateCache(list);
if (value == null) { if (list == null) {
setData(null); setData(null);
return; return;
} }
ValueSerializer<V> serializer = serializer(); ValueSerializer<V> serializer = serializer();
if (serializer == null) return; if (serializer == null) return;
List<Object> data = new ArrayList<>(); List<Object> data = new ArrayList<>();
for (V val : value) { for (V val : list) {
if (val == null) continue; if (val == null) continue;
try { try {
data.add(serializer.serialize(holder(), paramType(), val)); data.add(serializer.serialize(holder(), paramType(), withValidated(val)));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); throwing(ex);
} }
} }
setData(data); setData(data);
@@ -17,7 +17,11 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> { 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) { public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
return new ConfigMapCreator<>(keyType, valueType); return new ConfigMapCreator<>(keyType, valueType);
@@ -38,7 +42,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
protected final @NotNull ValueAdapter<K> keyAdapter; protected final @NotNull ValueAdapter<K> keyAdapter;
protected final @NotNull ValueAdapter<V> valueAdapter; protected final @NotNull ValueAdapter<V> valueAdapter;
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest, public ConfiguredMap(@NotNull ValueManifest<Map<K, V>, V> manifest,
@NotNull Supplier<? extends Map<K, V>> constructor, @NotNull Supplier<? extends Map<K, V>> constructor,
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) { @NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
super(manifest); super(manifest);
@@ -73,6 +77,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
// If the value is expired, we need to update it // If the value is expired, we need to update it
Map<K, V> map = createMap(); Map<K, V> map = createMap();
try {
ConfigureSection section = config().getSection(path()); ConfigureSection section = config().getSection(path());
if (section == null) return getDefaultFirst(map); if (section == null) return getDefaultFirst(map);
@@ -80,8 +85,9 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
if (keys.isEmpty()) return getDefaultFirst(map); if (keys.isEmpty()) return getDefaultFirst(map);
ValueParser<K> keyParser = parserFor(keyAdapter); ValueParser<K> keyParser = parserFor(keyAdapter);
if (keyParser == null) return getDefaultFirst(map);
ValueParser<V> valueParser = parserFor(valueAdapter); ValueParser<V> valueParser = parserFor(valueAdapter);
if (keyParser == null || valueParser == null) return getDefaultFirst(map); if (valueParser == null) return getDefaultFirst(map);
for (String dataKey : keys) { for (String dataKey : keys) {
Object dataVal = section.get(dataKey); Object dataVal = section.get(dataKey);
@@ -89,11 +95,14 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
try { try {
K key = keyParser.parse(holder(), keyType(), dataKey); K key = keyParser.parse(holder(), keyType(), dataKey);
V value = valueParser.parse(holder(), valueType(), dataVal); V value = valueParser.parse(holder(), valueType(), dataVal);
map.put(key, value); map.put(key, withValidated(value));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(path + "." + dataKey, e);
} }
} }
} catch (Exception ex) {
throwing(ex);
}
return updateCache(map); return updateCache(map);
} }
@@ -115,9 +124,11 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
return; return;
} }
try {
ValueSerializer<K> keySerializer = serializerFor(keyAdapter); ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
if (keySerializer == null) return;
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter); ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
if (keySerializer == null || valueSerializer == null) return; if (valueSerializer == null) return;
Map<Object, Object> data = new LinkedHashMap<>(); Map<Object, Object> data = new LinkedHashMap<>();
@@ -125,13 +136,16 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
try { try {
data.put( data.put(
keySerializer.serialize(holder(), keyType(), entry.getKey()), keySerializer.serialize(holder(), keyType(), entry.getKey()),
valueSerializer.serialize(holder(), valueType(), entry.getValue()) valueSerializer.serialize(holder(), valueType(), withValidated(entry.getValue()))
); );
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(path + "." + entry.getKey(), e);
} }
} }
setData(data); setData(data);
} catch (Exception ex) {
throwing(ex);
}
} }
public <T> @NotNull T handle(Function<Map<K, V>, T> function) { public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
@@ -5,6 +5,8 @@ import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer; import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.value.ConfigValueBuilder; import cc.carm.lib.configuration.builder.value.ConfigValueBuilder;
import cc.carm.lib.configuration.builder.value.SourceValueBuilder;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CachedConfigValue; import cc.carm.lib.configuration.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -12,16 +14,54 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ConfiguredValue<V> extends CachedConfigValue<V> { public class ConfiguredValue<V> extends CachedConfigValue<V, V> {
/**
* Create a new value builder.
*
* @param type The type of the value.
* @param <V> The type of the value.
* @return a {@link ConfigValueBuilder} with the specified type.
*/
public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) { public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) {
return new ConfigValueBuilder<>(ValueType.of(type)); return new ConfigValueBuilder<>(ValueType.of(type));
} }
/**
* Create a new value builder.
*
* @param type The type of the value.
* @param <V> The type of the value.
* @return a {@link ConfigValueBuilder} with the specified type.
*/
public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) { public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) {
return new ConfigValueBuilder<>(type); return new ConfigValueBuilder<>(type);
} }
/**
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
*
* @param registeredType The type of the value.
* @param <V> The type of the value.
* @return a {@link SourceValueBuilder} with the specified registered type.
* @see ValueAdapter
*/
public static <V> SourceValueBuilder<V, V> with(@NotNull Class<V> registeredType) {
return with(ValueType.of(registeredType));
}
/**
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
*
* @param registeredType The type of the value.
* @param <V> The type of the value.
* @return a {@link SourceValueBuilder} with the specified registered type.
* @see ValueAdapter
*/
public static <V> SourceValueBuilder<V, V> with(@NotNull ValueType<V> registeredType) {
return new ConfigValueBuilder<>(registeredType).from(registeredType);
}
public static <V> ConfiguredValue<V> of(@NotNull V defaults) { public static <V> ConfiguredValue<V> of(@NotNull V defaults) {
return of(ValueType.of(defaults), () -> defaults); return of(ValueType.of(defaults), () -> defaults);
} }
@@ -51,7 +91,7 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
); );
} }
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest, public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V, V> manifest,
@Nullable ValueParser<V> parser, @Nullable ValueParser<V> parser,
@Nullable ValueSerializer<V> serializer) { @Nullable ValueSerializer<V> serializer) {
ValueAdapter<V> adapter = new ValueAdapter<>(manifest.type()); ValueAdapter<V> adapter = new ValueAdapter<>(manifest.type());
@@ -60,13 +100,13 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
return of(manifest, adapter); return of(manifest, adapter);
} }
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) { public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V, V> manifest, @NotNull ValueAdapter<V> adapter) {
return new ConfiguredValue<>(manifest, adapter); return new ConfiguredValue<>(manifest, adapter);
} }
protected final @NotNull ValueAdapter<V> adapter; protected final @NotNull ValueAdapter<V> adapter;
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) { public ConfiguredValue(@NotNull ValueManifest<V, V> manifest, @NotNull ValueAdapter<V> adapter) {
super(manifest); super(manifest);
this.adapter = adapter; this.adapter = adapter;
} }
@@ -97,18 +137,19 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
if (!cacheExpired()) return getCachedOrDefault(); if (!cacheExpired()) return getCachedOrDefault();
// Data that is outdated and needs to be parsed again. // Data that is outdated and needs to be parsed again.
try {
Object data = getData(); Object data = getData();
if (data == null) return defaults(); if (data == null) return defaults();
ValueParser<V> parser = parser(); ValueParser<V> parser = parser();
if (parser == null) return defaults(); // No parser, return default value. if (parser == null) return defaults(); // No parser, return default value.
try {
// If there are no errors, update the cache and return. // If there are no errors, update the cache and return.
return updateCache(parser.parse(holder(), type(), data)); V parsed = parser.parse(holder(), type(), data);
return updateCache(withValidated(parsed));
} catch (Exception e) { } catch (Exception e) {
// There was a parsing error, prompted and returned the default value. // There was a validate or parsing error, prompted and returned the default value.
e.printStackTrace(); throwing(e);
return defaults(); return defaults();
} }
@@ -121,20 +162,20 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
* @param value The value to be set * @param value The value to be set
*/ */
@Override @Override
public void set(V value) { public void set(@Nullable V value) {
updateCache(value); // Update cache updateCache(value); // Update cache
if (value == null) { if (value == null) {
setData(null); setData(null);
return; return;
} }
try {
ValueSerializer<V> serializer = serializer(); ValueSerializer<V> serializer = serializer();
if (serializer == null) return; // No serializer, do nothing. if (serializer == null) return; // No serializer, do nothing.
try { setData(serializer.serialize(holder(), type(), withValidated(value)));
setData(serializer.serialize(holder(), type(), value));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); throwing(e);
} }
} }
@@ -9,7 +9,7 @@
//import java.util.LinkedList; //import java.util.LinkedList;
//import java.util.List; //import java.util.List;
// //
///** /// **
// * @author Chris2018998 // * @author Chris2018998
// */ // */
//public class OffsetUtil { //public class OffsetUtil {
@@ -25,15 +25,15 @@
// } catch (NoSuchFieldException | IllegalAccessException e) { // } catch (NoSuchFieldException | IllegalAccessException e) {
// e.printStackTrace(); // e.printStackTrace();
// } // }
//// try { /// / try {
//// unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> { /// / unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
//// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); /// / Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
//// theUnsafe.setAccessible(true); /// / theUnsafe.setAccessible(true);
//// return (Unsafe) theUnsafe.get(null); /// / return (Unsafe) theUnsafe.get(null);
//// }); /// / });
//// } catch (Throwable e) { /// / } catch (Throwable e) {
//// System.err.println("Unable to load unsafe"); /// / System.err.println("Unable to load unsafe");
//// } /// / }
// } // }
// //
// public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) { // public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) {
+159 -8
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.1.0</version> <version>4.1.7</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -13,6 +13,11 @@
<maven.compiler.target>${project.jdk.version}</maven.compiler.target> <maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.javadoc.skip>true</maven.javadoc.skip>
<maven.deploy.skip>true</maven.deploy.skip>
<deps.mysql-driver.version>8.0.33</deps.mysql-driver.version>
<log4j.version>2.25.0</log4j.version>
</properties> </properties>
<artifactId>configured-demo</artifactId> <artifactId>configured-demo</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -23,31 +28,112 @@
<groupId>${project.parent.groupId}</groupId> <groupId>${project.parent.groupId}</groupId>
<artifactId>configured-core</artifactId> <artifactId>configured-core</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>${project.parent.groupId}</groupId> <groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-commentable</artifactId> <artifactId>configured-feature-commentable</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>${project.parent.groupId}</groupId> <groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-versioned</artifactId> <artifactId>configured-feature-versioned</artifactId>
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<scope>compile</scope> </dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-validators</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-kotlin</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-yaml</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-gson</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-sql</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easysql-beecp</artifactId>
<version>0.4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${deps.mysql-driver.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-mongodb</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-hocon</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
@@ -60,6 +146,71 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/main/java</source>
<source>target/generated-sources/annotations</source>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -3,7 +3,6 @@ package cc.carm.lib.configuration.demo;
import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath; import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.HeaderComments; import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"}) @HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
@@ -16,17 +15,17 @@ public interface DatabaseConfiguration extends Configuration {
"- MySQL(新): com.mysql.cj.jdbc.Driver", "- MySQL(新): com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver", "- MariaDB(推荐): org.mariadb.jdbc.Driver",
}) })
ConfigValue<String> DRIVER_NAME = ConfiguredValue.of( ConfiguredValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver" String.class, "com.mysql.cj.jdbc.Driver"
); );
ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1"); ConfiguredValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306); ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft"); ConfiguredValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root"); ConfiguredValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password"); ConfiguredValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false"); ConfiguredValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
static String buildJDBC() { static String buildJDBC() {
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get()); return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
@@ -1,11 +1,13 @@
package cc.carm.lib.configuration.demo.tests; package cc.carm.lib.configuration.demo.tests;
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration; import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
import cc.carm.lib.configuration.demo.tests.conf.KotlinConfiguration;
import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig; import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig;
import cc.carm.lib.configuration.demo.tests.model.UserRecord; import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.TestOnly;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -80,6 +82,22 @@ public class ConfigurationTest {
} }
public static void testKotlin(ConfigurationHolder<?> provider) {
provider.initialize(KotlinConfiguration.class);
System.out.println("> Test Kotlin value before:");
System.out.println(KotlinConfiguration.INSTANCE.getLINKED_MAP().get());
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("Language", "Kotlin");
System.out.println("> Test Kotlin value -> " + map);
KotlinConfiguration.INSTANCE.getLINKED_MAP().set(map);
System.out.println("> Test Kotlin value after:");
System.out.println(KotlinConfiguration.INSTANCE.getLINKED_MAP().get());
}
public static void save(ConfigurationHolder<?> provider) { public static void save(ConfigurationHolder<?> provider) {
try { try {
provider.save(); provider.save();
@@ -4,7 +4,6 @@ import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.*; import cc.carm.lib.configuration.annotation.*;
import cc.carm.lib.configuration.demo.tests.model.ItemStack; import cc.carm.lib.configuration.demo.tests.model.ItemStack;
import cc.carm.lib.configuration.demo.tests.model.UserRecord; import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredList; import cc.carm.lib.configuration.value.standard.ConfiguredList;
import cc.carm.lib.configuration.value.standard.ConfiguredMap; import cc.carm.lib.configuration.value.standard.ConfiguredMap;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@@ -26,15 +25,15 @@ public interface DemoConfiguration extends Configuration {
@ConfigPath(root = true) @ConfigPath(root = true)
@ConfigVersion(2) @ConfigVersion(2)
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 2.0D); ConfiguredValue<Double> VERSION = ConfiguredValue.of(Double.class, 2.0D);
@ConfigPath(root = true) @ConfigPath(root = true)
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"}) @FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L); ConfiguredValue<Long> TEST_NUMBER = ConfiguredValue.with(Long.class).defaults(123456789L).build();
@HeaderComments({"枚举类型测试"}) @HeaderComments({"枚举类型测试"})
@FooterComments({"上述的枚举内容本质上是通过STRING解析的"}) @FooterComments({"上述的枚举内容本质上是通过STRING解析的"})
ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); ConfiguredValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
@HeaderComments({"空值测试"}) @HeaderComments({"空值测试"})
@InlineComment("空值Inline注释") @InlineComment("空值Inline注释")
@@ -72,7 +71,7 @@ public interface DemoConfiguration extends Configuration {
class SUB implements Configuration { class SUB implements Configuration {
@ConfigPath(value = "uuid-value", root = true) @ConfigPath(value = "uuid-value", root = true)
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue public static final ConfiguredValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
.builderOf(UUID.class).fromString() .builderOf(UUID.class).fromString()
.parse((holder, data) -> UUID.fromString(data)) .parse((holder, data) -> UUID.fromString(data))
.build(); .build();
@@ -2,12 +2,13 @@ package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.HeaderComments; import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.annotation.ValueRange;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments("Inner Test") @HeaderComments("service")
public class InstanceConfig implements Configuration { public class InstanceConfig implements Configuration {
public final ConfigValue<Double> STATUS = ConfiguredValue.of(1.0D); @ValueRange(min = 0, max = 100, message = "The value must be between 0 and 100")
public final ConfiguredValue<Double> STATUS = ConfiguredValue.of(1.0D);
} }
@@ -0,0 +1,32 @@
package cc.carm.lib.configuration.demo.tests.conf
import cc.carm.lib.configuration.Configuration
import cc.carm.lib.configuration.annotation.ConfigPath
import cc.carm.lib.configuration.annotation.ConfigVersion
import cc.carm.lib.configuration.kotlin.value.*
@ConfigPath(root = true)
object KotlinConfiguration : Configuration {
@ConfigVersion(1)
val VERSION = valueFrom(Double::class) {
defaults(1.0)
}
val USER_LIST = listFrom(String::class) {
defaults("Carm Jos")
}
val NICKNAME = mapFrom(String::class, ::mutableMapOf) {
defaultMap(mapOf("Carm Jos" to "Carm"))
parse { v -> v }
serialize { v -> v }
}
val LINKED_MAP = linkedMapFrom(String::class) {
parse { value ->
value
}
serialize { v -> v }
defaults("key", "value")
}
}
@@ -6,7 +6,6 @@ import cc.carm.lib.configuration.annotation.FooterComments;
import cc.carm.lib.configuration.annotation.HeaderComments; import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.annotation.InlineComment; import cc.carm.lib.configuration.annotation.InlineComment;
import cc.carm.lib.configuration.demo.tests.model.UserRecord; import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import java.util.UUID; import java.util.UUID;
@@ -20,7 +19,7 @@ public class RegistryConfig implements Configuration {
@FooterComments({"12313213212"}) @FooterComments({"12313213212"})
@InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。 @InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。
@InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。 @InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。
public final ConfigValue<UserRecord> OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection() public final ConfiguredValue<UserRecord> OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection()
.defaults(new UserRecord("Carm", UUID.randomUUID())) .defaults(new UserRecord("Carm", UUID.randomUUID()))
.parse((holder, section) -> UserRecord.deserialize(section)) .parse((holder, section) -> UserRecord.deserialize(section))
.serialize((holder, data) -> data.serialize()).build(); .serialize((holder, data) -> data.serialize()).build();
@@ -33,11 +33,11 @@ public class ItemStack {
return amount; return amount;
} }
public String getName() { public @Nullable String getName() {
return name; return name;
} }
public List<String> getLore() { public @Nullable List<String> getLore() {
return lore; return lore;
} }
@@ -49,11 +49,11 @@ public class ItemStack {
this.amount = amount; this.amount = amount;
} }
public void setName(String name) { public void setName(@Nullable String name) {
this.name = name; this.name = name;
} }
public void setLore(List<String> lore) { public void setLore(@Nullable List<String> lore) {
this.lore = lore; this.lore = lore;
} }
@@ -1,15 +1,13 @@
package config; package cc.carm.lib.configuration.tests.test;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest; import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
public class JSONConfigTest { public class JSONConfigTests {
protected final ConfigurationHolder<?> holder = JSONConfigFactory protected final ConfigurationHolder<?> holder = JSONConfigFactory
.from(new File("target"), "config.json") .from(new File("target"), "config.json")
@@ -18,12 +16,6 @@ public class JSONConfigTest {
@Test @Test
public void onTest() { public void onTest() {
ConfigValue<Boolean> EXAMPLE = ConfiguredValue.of(false);
EXAMPLE.initialize(this.holder, "example");
System.out.println("Example: " + EXAMPLE.get());
ConfigurationTest.testDemo(this.holder); ConfigurationTest.testDemo(this.holder);
ConfigurationTest.testInner(this.holder); ConfigurationTest.testInner(this.holder);
@@ -1,11 +1,11 @@
package yaml.test; package cc.carm.lib.configuration.tests.test;
import cc.carm.lib.configuration.commentable.Commentable;
import cc.carm.lib.configuration.commentable.CommentableMeta; import cc.carm.lib.configuration.commentable.CommentableMeta;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest; import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory; import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory;
import cc.carm.lib.configuration.source.yaml.YAMLSource; import cc.carm.lib.configuration.source.yaml.YAMLSource;
import cc.carm.lib.configuration.validators.Validators;
import org.junit.Test; import org.junit.Test;
import java.util.List; import java.util.List;
@@ -14,11 +14,13 @@ import java.util.Map;
public class YamlTests { public class YamlTests {
@Test @Test
public void test() { public void test() throws Exception {
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/tests.yml") ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/tests.yml")
.resourcePath("configs/sample.yml").build(); .resourcePath("configs/sample.yml").build();
Validators.activate(holder);
ConfigurationTest.testDemo(holder); ConfigurationTest.testDemo(holder);
ConfigurationTest.testInner(holder); ConfigurationTest.testInner(holder);
@@ -42,5 +44,13 @@ public class YamlTests {
ConfigurationTest.save(holder); ConfigurationTest.save(holder);
} }
@Test
public void testKotlin() {
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/kotlin.yml").build();
ConfigurationTest.testKotlin(holder);
ConfigurationTest.save(holder);
}
} }
@@ -1,4 +1,4 @@
package config; package cc.carm.lib.configuration.tests.test.mongodb;
import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@@ -1,4 +1,4 @@
package config; package cc.carm.lib.configuration.tests.test.mongodb;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest; import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
@@ -1,4 +1,4 @@
package config; package cc.carm.lib.configuration.tests.test.sql;
import cc.carm.lib.configuration.demo.DatabaseConfiguration; import cc.carm.lib.configuration.demo.DatabaseConfiguration;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest; import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="config.SQLConfigTest"> <Configuration status="WARN" packages="cc.carm.lib.configuration.tests">
<Appenders> <Appenders>
<console name="Console" target="SYSTEM_OUT"> <console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/> <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -1,7 +1,5 @@
package cc.carm.lib.configuration.source.option; package cc.carm.lib.configuration.source.option;
import cc.carm.lib.configuration.source.option.ConfigurationOption;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId>
<version>4.1.7</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
<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>
<kotlin.version>2.2.0</kotlin.version>
</properties>
<artifactId>configured-feature-kotlin</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>configured-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
<version>2.0.0</version>
<executions>
<execution>
<phase>pre-site</phase>
<goals>
<goal>dokka</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,29 @@
package cc.carm.lib.configuration.kotlin.value
import cc.carm.lib.configuration.adapter.ValueType
import cc.carm.lib.configuration.builder.list.SourceListBuilder
import cc.carm.lib.configuration.value.standard.ConfiguredList
import kotlin.reflect.KClass
inline fun <S : Any, reified V> listFrom(
clazz: KClass<S>, block: (SourceListBuilder<S, V>.() -> Unit)
): ConfiguredList<V> {
return listFrom(clazz.java, block)
}
inline fun <S : Any, reified V> listFrom(
clazz: Class<S>, block: (SourceListBuilder<S, V>.() -> Unit)
): ConfiguredList<V> {
return listFrom(ValueType.of(clazz), block)
}
inline fun <S : Any, reified V> listFrom(
valueType: ValueType<S>, block: (SourceListBuilder<S, V>.() -> Unit)
): ConfiguredList<V> {
val configBuilder = ConfiguredList.builderOf(V::class.java)
val sourceValueBuilder: SourceListBuilder<S, V> = if (valueType.rawType == String::class.java) {
@Suppress("UNCHECKED_CAST")
configBuilder.fromString() as SourceListBuilder<S, V>
} else configBuilder.from(valueType)
return sourceValueBuilder.also(block).build()
}
@@ -0,0 +1,107 @@
package cc.carm.lib.configuration.kotlin.value
import cc.carm.lib.configuration.adapter.ValueType
import cc.carm.lib.configuration.builder.map.SourceMapBuilder
import cc.carm.lib.configuration.value.standard.ConfiguredMap
import java.util.*
import kotlin.reflect.KClass
inline fun <S : Any, reified K, reified V> hashmapFrom(
clazz: KClass<S>,
block: SourceMapBuilder<HashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return hashmapFrom(clazz.java, block)
}
inline fun <S : Any, reified K, reified V> hashmapFrom(
clazz: Class<S>,
block: SourceMapBuilder<HashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return hashmapFrom(ValueType.of(clazz), block)
}
inline fun <S : Any, reified K, reified V> hashmapFrom(
valueType: ValueType<S>,
block: SourceMapBuilder<HashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
val mapCreator = ConfiguredMap.builderOf(K::class.java, V::class.java)
val sourceValueBuilder: SourceMapBuilder<HashMap<K, V>, S, K, V> = mapCreator.asHashMap().from(valueType)
return sourceValueBuilder.also(block).build()
}
inline fun <S : Any, reified K, reified V> linkedMapFrom(
clazz: KClass<S>,
block: SourceMapBuilder<LinkedHashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return linkedMapFrom(clazz.java, block)
}
inline fun <S : Any, reified K, reified V> linkedMapFrom(
clazz: Class<S>,
block: SourceMapBuilder<LinkedHashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return linkedMapFrom(ValueType.of(clazz), block)
}
inline fun <S : Any, reified K, reified V> linkedMapFrom(
valueType: ValueType<S>,
block: SourceMapBuilder<LinkedHashMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
val mapCreator = ConfiguredMap.builderOf(K::class.java, V::class.java)
val sourceValueBuilder: SourceMapBuilder<LinkedHashMap<K, V>, S, K, V> = mapCreator.asLinkedMap().from(valueType)
return sourceValueBuilder.also(block).build()
}
inline fun <S : Any, reified K, reified V> treeMapFrom(
clazz: KClass<S>,
block: SourceMapBuilder<TreeMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return treeMapFrom(clazz.java, block)
}
inline fun <S : Any, reified K, reified V> treeMapFrom(
clazz: Class<S>,
block: SourceMapBuilder<TreeMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return treeMapFrom(ValueType.of(clazz), block)
}
inline fun <S : Any, reified K, reified V> treeMapFrom(
valueType: ValueType<S>,
block: SourceMapBuilder<TreeMap<K, V>, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
val mapCreator = ConfiguredMap.builderOf(K::class.java, V::class.java)
val sourceValueBuilder: SourceMapBuilder<TreeMap<K, V>, S, K, V> = mapCreator.asTreeMap().from(valueType)
return sourceValueBuilder.also(block).build()
}
inline fun <reified MAP : Map<K, V>, S : Any, reified K, reified V> mapFrom(
clazz: KClass<S>,
noinline map: () -> MAP,
block: SourceMapBuilder<MAP, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return mapFrom(clazz.java, map, block)
}
inline fun <reified MAP : Map<K, V>, S : Any, reified K, reified V> mapFrom(
clazz: Class<S>,
noinline map: () -> MAP,
block: SourceMapBuilder<MAP, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
return mapFrom(ValueType.of(clazz), map, block)
}
inline fun <reified MAP : Map<K, V>, S : Any, reified K, reified V> mapFrom(
valueType: ValueType<S>,
noinline map: () -> MAP,
block: SourceMapBuilder<MAP, S, K, V>.() -> Unit
): ConfiguredMap<K, V> {
val mapCreator = ConfiguredMap.builderOf(K::class.java, V::class.java)
val sourceValueBuilder: SourceMapBuilder<MAP, S, K, V> = mapCreator.constructor(map).from(valueType)
return sourceValueBuilder.also(block).build()
}
fun <MAP : Map<K, V>, S, K, V> SourceMapBuilder<MAP, S, K, V>.defaultMap(map: MAP): SourceMapBuilder<MAP, S, K, V> {
return defaults(map)
}
@@ -0,0 +1,36 @@
package cc.carm.lib.configuration.kotlin.value
import cc.carm.lib.configuration.adapter.ValueType
import cc.carm.lib.configuration.builder.value.SourceValueBuilder
import cc.carm.lib.configuration.value.standard.ConfiguredValue
import kotlin.reflect.KClass
inline fun <S : Any, reified T> valueFrom(
clazz: KClass<S>, block: (SourceValueBuilder<S, T>.() -> Unit)
): ConfiguredValue<T> {
return valueFrom(clazz.java, block)
}
inline fun <S : Any, reified V> valueFrom(
clazz: Class<S>, block: (SourceValueBuilder<S, V>.() -> Unit)
): ConfiguredValue<V> {
return valueFrom(ValueType.of(clazz), block)
}
inline fun <S : Any, reified V> valueFrom(
valueType: ValueType<S>, block: (SourceValueBuilder<S, V>.() -> Unit)
): ConfiguredValue<V> {
val configBuilder = ConfiguredValue.builderOf(V::class.java)
val sourceValueBuilder: SourceValueBuilder<S, V> = if (valueType.rawType == String::class.java) {
@Suppress("UNCHECKED_CAST")
configBuilder.fromString() as SourceValueBuilder<S, V>
} else configBuilder.from(valueType)
sourceValueBuilder.parse { holder, data ->
holder.deserialize(V::class.java, data)
}
sourceValueBuilder.serialize { holder, data ->
@Suppress("UNCHECKED_CAST")
holder.serialize(data) as? S
}
return sourceValueBuilder.also(block).build()
}
+66
View File
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId>
<version>4.1.7</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>configured-feature-record</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-temp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>16</source>
<target>16</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,114 @@
package cc.carm.lib.configured.adapter.record;
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.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
public class RecordAdapter<T extends Record> extends ValueAdapter<T> {
public static void register(ConfigurationHolder<?> holder) {
holder.adapters().register(of(Record.class));
}
public static <R extends Record> RecordAdapter<R> of(@NotNull Class<R> type) {
return of(ValueType.of(type));
}
public static <R extends Record> RecordAdapter<R> of(@NotNull ValueType<R> type) {
return new RecordAdapter<>(type);
}
public RecordAdapter(@NotNull ValueType<T> type) {
super(type, serializer(type), parser(type));
}
public static <R extends Record> ValueSerializer<R> serializer(@NotNull ValueType<R> type) {
return (holder, type1, r) -> toMap(holder, r);
}
@SuppressWarnings("unchecked")
public static <R extends Record> ValueParser<R> parser(@NotNull ValueType<R> type) {
return (holder, valueType, value) -> {
if (value instanceof ConfigureSection section) {
return fromMap(holder, (Class<R>) valueType.getRawType(), section.asMap());
} else if (value instanceof Map<?, ?> map) {
return fromMap(holder, (Class<R>) valueType.getRawType(), (Map<String, Object>) map);
} else return null;
};
}
public static <R extends Record> Map<String, Object> toMap(
@NotNull ConfigurationHolder<?> holder, @NotNull R record
) throws Exception {
Map<String, Object> map = new LinkedHashMap<>();
Class<?> recordClass = record.getClass();
if (!recordClass.isRecord()) {
throw new IllegalArgumentException("Object is not a record");
}
try {
for (RecordComponent component : recordClass.getRecordComponents()) {
String name = component.getName();
Method accessor = component.getAccessor();
accessor.setAccessible(true);
Object value = accessor.invoke(record);
map.put(name, serializeValue(holder, value));
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to convert record to map", e);
}
return map;
}
public static <R extends Record> R fromMap(
@NotNull ConfigurationHolder<?> holder,
@NotNull Class<R> type, @NotNull Map<String, Object> data
) throws Exception {
RecordComponent[] components = type.getRecordComponents();
Object[] args = new Object[components.length];
for (int i = 0; i < components.length; i++) {
RecordComponent component = components[i];
args[i] = parseValue(holder, component, data.get(component.getName()));
}
return createInstance(type, args);
}
@SuppressWarnings("unchecked")
private static Object parseValue(ConfigurationHolder<?> holder, RecordComponent component, Object value) throws Exception {
if (value == null) return null;
if (component.getType().isRecord()) {
return fromMap(holder, component.getType().asSubclass(Record.class), (Map<String, Object>) value);
}
ValueType<?> valueType = ValueType.of(component.getGenericType());
return holder.deserialize(valueType, value);
}
private static Object serializeValue(ConfigurationHolder<?> holder, Object value) throws Exception {
if (value == null) return null;
if (value.getClass().isRecord()) {
return toMap(holder, (Record) value);
}
return holder.serialize(value);
}
private static <T> T createInstance(Class<T> t, Object[] args) throws Exception {
Class<?>[] paramTypes = Arrays.stream(t.getRecordComponents())
.map(RecordComponent::getType).toArray(Class[]::new);
Constructor<T> constructor = t.getDeclaredConstructor(paramTypes);
constructor.setAccessible(true); // Make sure the constructor is accessible
return constructor.newInstance(args);
}
}
@@ -0,0 +1,127 @@
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.temp.TempConfigFactory;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.lib.configured.adapter.record.RecordAdapter;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class RecordTest {
@ConfigPath(root = true)
interface ConfigA extends Configuration {
ConfiguredValue<Device> VAL = ConfiguredValue.of(new Device(
"device1",
"My Device",
UUID.fromString("123e4567-e89b-12d3-a456-426614174000"),
new Chip("chip1", null),
Arrays.asList(
new User("Alice", 30),
new User("Bob", 25)
),
Map.of(
"Cloud", new Connection("cloud.example.com", 443),
"Local", new Connection("127.0.0.1", 8080)
)
));
}
@ConfigPath(root = true)
interface ConfigB extends Configuration {
ConfiguredValue<Device> VAL = ConfiguredValue.of(Device.class);
}
@Test
public void test() {
ConfigurationHolder<?> holder = TempConfigFactory.create().build();
RecordAdapter.register(holder);
holder.initialize(ConfigA.class);
System.out.println("Device ID: " + ConfigA.VAL.resolve().id());
System.out.println("Device Name: " + ConfigA.VAL.resolve().name());
System.out.println("Device Serial: " + ConfigA.VAL.resolve().serial());
System.out.println("Chip ID: " + ConfigA.VAL.resolve().chip().id());
System.out.println("Chip Serial: " + ConfigA.VAL.resolve().chip().serialNumber());
for (User user : ConfigA.VAL.resolve().users()) {
System.out.println("Another Users: " + user.name() + ", Age: " + user.age());
}
printMap(holder.config().asMap(), 0);
// try {
// List<User> parsed = holder.deserialize(ValueType.ofList(User.class), holder.config().getList("val.users"));
// System.out.println("Parsed Users: " + parsed);
// } catch (Exception e) {
// e.printStackTrace();
// }
ConfigurationHolder<?> anotherHolder = TempConfigFactory.create().defaults(() -> holder.config().asMap()).build();
RecordAdapter.register(anotherHolder);
anotherHolder.initialize(ConfigB.class);
System.out.println("Another Device ID: " + ConfigB.VAL.resolve().id());
System.out.println("Another Device Name: " + ConfigB.VAL.resolve().name());
System.out.println("Another Device Serial: " + ConfigB.VAL.resolve().serial());
System.out.println("Another Chip ID: " + ConfigB.VAL.resolve().chip().id());
System.out.println("Another Chip Serial: " + ConfigB.VAL.resolve().chip().serialNumber());
System.out.println("users: " + ConfigB.VAL.resolve().users().size());
for (User user : ConfigB.VAL.resolve().users()) {
System.out.println("Another Users: " + user.name() + ", Age: " + user.age());
}
ConfigB.VAL.resolve().connections.forEach((k, v) -> {
System.out.println("Connection " + k + ": " + v.address() + ":" + v.port());
});
}
record User(String name, int age) {
}
record Connection(String address, int port) {
}
record Device(String id, String name, UUID serial, Chip chip,
List<User> users,
Map<String, Connection> connections) {
}
record Chip(String id, @Nullable String serialNumber) {
}
static void printMap(Map<String, Object> map, int indent) {
String indentStr = " ".repeat(indent);
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Map<?, ?> subMap) {
System.out.println(indentStr + entry.getKey() + ":");
printMap((Map<String, Object>) subMap, indent + 2);
} else if (entry.getValue() instanceof List<?> subList) {
System.out.println(indentStr + entry.getKey() + ":");
for (Object item : subList) {
if (item instanceof Map<?, ?> itemMap) {
System.out.println(indentStr + " - ");
printMap((Map<String, Object>) itemMap, indent + 2);
} else {
System.out.println(indentStr + " - " + item);
}
}
} else {
System.out.println(indentStr + entry.getKey() + ": " + entry.getValue());
}
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -74,7 +74,7 @@ public class ImmutableSection implements ConfigureSection {
@Override @Override
public @Nullable ConfigureSection getSection(@NotNull String path) { public @Nullable ConfigureSection getSection(@NotNull String path) {
ConfigureSection get = raw().getSection(path); ConfigureSection get = raw().getSection(path);
if (get != null && !(get instanceof ImmutableSection)) { if (!(get instanceof ImmutableSection)) {
return new ImmutableSection(this, get); return new ImmutableSection(this, get);
} }
return get; return get;
@@ -166,12 +166,12 @@ public class ImmutableSection implements ConfigureSection {
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) { public <T> @Nullable T get(@NotNull String path, T defaults, @NotNull Class<T> clazz) {
return raw().get(path, defaults, clazz); return raw().get(path, defaults, clazz);
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull DataFunction<Object, T> parser) { public <T> @Nullable T get(@NotNull String path, T defaultValue, @NotNull DataFunction<Object, T> parser) {
return raw().get(path, defaultValue, parser); return raw().get(path, defaultValue, parser);
} }
@@ -350,6 +350,11 @@ public class ImmutableSection implements ConfigureSection {
return raw().getCharList(path); return raw().getCharList(path);
} }
@Override
public @NotNull List<ConfigureSection> getSectionList(@NotNull String path) {
return raw().getSectionList(path);
}
@Override @Override
public <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path, @NotNull Supplier<C> constructor, @NotNull DataFunction<Object, T> parser) { public <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path, @NotNull Supplier<C> constructor, @NotNull DataFunction<Object, T> parser) {
return raw().getCollection(path, constructor, parser); return raw().getCollection(path, constructor, parser);
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -7,7 +7,6 @@ import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.standard.ConfiguredValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.lib.configuration.value.text.data.TextContents; import cc.carm.lib.configuration.value.text.data.TextContents;
import cc.carm.lib.configuration.value.text.function.TextDispatcher;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -22,7 +21,7 @@ import java.util.function.Consumer;
*/ */
public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents> { public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents> {
public static <M, R> Builder<M, R, ?> builder() { public static <M, R> Builder<M, R, ?> create() {
return new StardardBuilder<>(); return new StardardBuilder<>();
} }
@@ -38,7 +37,7 @@ public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents>
protected final @NotNull String[] params; // The parameters of the message. protected final @NotNull String[] params; // The parameters of the message.
public ConfiguredText(@NotNull ValueManifest<TextContents> manifest, public ConfiguredText(@NotNull ValueManifest<TextContents, TextContents> manifest,
@NotNull BiFunction<RECEIVER, String, String> parser, @NotNull BiFunction<RECEIVER, String, String> parser,
@NotNull BiFunction<RECEIVER, String, MSG> compiler, @NotNull BiFunction<RECEIVER, String, MSG> compiler,
@NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher, @NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher,
@@ -50,7 +49,7 @@ public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents>
this.params = params; this.params = params;
} }
public TextDispatcher<MSG, RECEIVER, ?> prepare(@NotNull Object... values) { public PreparedText<MSG, RECEIVER> prepare(@NotNull Object... values) {
return new PreparedText<MSG, RECEIVER>(resolve(), this.params) return new PreparedText<MSG, RECEIVER>(resolve(), this.params)
.parser(this.parser).compiler(this.compiler) .parser(this.parser).compiler(this.compiler)
.dispatcher(this.dispatcher).placeholders(values); .dispatcher(this.dispatcher).placeholders(values);
@@ -121,7 +120,7 @@ public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents>
} }
public abstract static class Builder<MSG, RECEIVER, SELF extends Builder<MSG, RECEIVER, SELF>> public abstract static class Builder<MSG, RECEIVER, SELF extends Builder<MSG, RECEIVER, SELF>>
extends AbstractConfigBuilder<TextContents, ConfiguredText<MSG, RECEIVER>, ConfigurationHolder<?>, SELF> { extends AbstractConfigBuilder<TextContents, TextContents, ConfiguredText<MSG, RECEIVER>, ConfigurationHolder<?>, SELF> {
protected @NotNull TextContents.Builder defaultBuilder = TextContents.builder(); protected @NotNull TextContents.Builder defaultBuilder = TextContents.builder();
protected @NotNull String[] params = new String[0]; protected @NotNull String[] params = new String[0];
@@ -1,52 +1,27 @@
package cc.carm.lib.configuration.value.text.function; package cc.carm.lib.configuration.value.text.function;
import cc.carm.lib.configuration.value.text.data.TextContents; import cc.carm.lib.configuration.value.text.data.TextContents;
import cc.carm.lib.configuration.value.text.function.common.AppendLineInserter;
import cc.carm.lib.configuration.value.text.function.common.OptionalLineInserter;
import cc.carm.lib.configuration.value.text.function.common.ParamReplacer;
import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter;
import cc.carm.lib.configuration.value.text.function.inserter.Insertable;
import cc.carm.lib.configuration.value.text.function.replacer.ContentReplacer;
import cc.carm.lib.configuration.value.text.function.replacer.Replaceable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEIVER, SELF>> { public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEIVER, SELF>>
implements Replaceable<RECEIVER, SELF>, Insertable<RECEIVER, SELF> {
/**
* Used to match the message insertion.
* <p>
* format:
* <br>- to insert parsed line {prefix}#content-id#{offset-above,offset-down}
* <br>- to insert original line {prefix}@content-id@{offset-above,offset-down}
* <br> original lines will not be parsed
* <br> example:
* <ul>
* <li>{- }#content-id#{1,1}</li>
* <li>@content-id@{1,1}</li>
* </ul>
*/
public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile(
"^(?:\\{(?<prefix>.*)})?(?<type>[#@])(?<id>.*)[#@](?:\\{(?<above>-?\\d+)(?:,(?<down>-?\\d+))?})?$"
);
/**
* Used to match the message which can be inserted
* <p>
* format:
* <br>- ?[id]Message content
* <br> example:
* <ul>
* <li>?[click]Click to use this item!</li>
* </ul>
*/
public static final @NotNull Pattern ENABLE_PATTERN = Pattern.compile(
"^\\?\\[(?<id>.+)](?<content>.*)$"
);
public static final @NotNull UnaryOperator<String> DEFAULT_PARAM_BUILDER = s -> "%(" + s + ")";
protected BiFunction<RECEIVER, String, String> parser = (receiver, value) -> value; protected BiFunction<RECEIVER, String, String> parser = (receiver, value) -> value;
protected String lineSeparator = System.lineSeparator(); protected String lineSeparator = System.lineSeparator();
@@ -54,16 +29,24 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
/** /**
* Used to store the placeholders of the message * Used to store the placeholders of the message
*/ */
protected @NotNull Map<String, Object> placeholders = new HashMap<>(); protected @NotNull ParamReplacer<RECEIVER> paramReplacer = ParamReplacer.defaults();
protected @NotNull UnaryOperator<String> paramFormatter = DEFAULT_PARAM_BUILDER;
protected @NotNull String[] params; protected @NotNull String[] params;
protected @NotNull List<ContentReplacer<RECEIVER>> replacers = new ArrayList<>();
protected @NotNull List<ContentInserter<RECEIVER>> inserters = new ArrayList<>();
/** /**
* Used to store the insertion of the message * Used to store the insertion of the message
*/ */
protected @NotNull Map<String, @Nullable Function<RECEIVER, List<String>>> insertion = new HashMap<>(); protected final @NotNull Map<String, @Nullable Function<RECEIVER, List<String>>> insertion = new HashMap<>();
protected boolean disableInsertion = false; protected boolean disableInsertion = false;
public ContentHandler() {
inserters.add(new AppendLineInserter<>(0));
inserters.add(new OptionalLineInserter<>(0));
}
public abstract SELF self(); public abstract SELF self();
/** /**
@@ -87,6 +70,11 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
return self(); return self();
} }
@Override
public boolean noneInsertion() {
return this.insertion.isEmpty();
}
/** /**
* Set the line separator for the text. * Set the line separator for the text.
* *
@@ -106,7 +94,7 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
* @return the current {@link ContentHandler} instance * @return the current {@link ContentHandler} instance
*/ */
public SELF placeholders(@NotNull Map<String, Object> placeholders) { public SELF placeholders(@NotNull Map<String, Object> placeholders) {
this.placeholders = placeholders; this.paramReplacer.set(placeholders);
return self(); return self();
} }
@@ -117,7 +105,7 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
* @return the current {@link ContentHandler} instance * @return the current {@link ContentHandler} instance
*/ */
public SELF placeholders(@NotNull Consumer<Map<String, Object>> consumer) { public SELF placeholders(@NotNull Consumer<Map<String, Object>> consumer) {
consumer.accept(this.placeholders); consumer.accept(this.paramReplacer.placeholders());
return self(); return self();
} }
@@ -128,7 +116,8 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
* @return the current {@link ContentHandler} instance * @return the current {@link ContentHandler} instance
*/ */
public SELF placeholders(@Nullable Object... values) { public SELF placeholders(@Nullable Object... values) {
return placeholders(map -> map.putAll(buildParams(this.paramFormatter, this.params, values))); this.paramReplacer.putAll(this.params, values);
return self();
} }
/** /**
@@ -139,7 +128,7 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
* @return the current {@link ContentHandler} instance * @return the current {@link ContentHandler} instance
*/ */
public SELF placeholder(@NotNull String key, @Nullable Object value) { public SELF placeholder(@NotNull String key, @Nullable Object value) {
this.placeholders.put(paramFormatter.apply(key), value); this.paramReplacer.put(key, value);
return self(); return self();
} }
@@ -155,49 +144,57 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
return self(); return self();
} }
/** @Override
* Insert the specific contents by the id. public List<ContentReplacer<RECEIVER>> replacers() {
* return this.replacers;
* @param id the id of the insertion text }
* @return the current {@link ContentHandler} instance
*/ @Override
public SELF insert(@NotNull String id) { public SELF replacer(@NotNull ContentReplacer<RECEIVER> replacer) {
this.insertion.put(id, null); this.replacers.add(replacer);
this.replacers.sort(ContentReplacer::compareTo);
return self(); return self();
} }
/** public SELF paramReplacer(@NotNull ParamReplacer<RECEIVER> paramReplacer) {
* Insert the specific contents by the id. this.paramReplacer = paramReplacer;
*
* @param id the id of the insertion text
* @param linesSupplier to supply the lines to insert
* @return the current {@link ContentHandler} instance
*/
public SELF insert(@NotNull String id, @NotNull Function<RECEIVER, List<String>> linesSupplier) {
this.insertion.put(id, linesSupplier);
return self(); return self();
} }
/** @Override
* Insert the specific contents by the id. public List<ContentInserter<RECEIVER>> inserters() {
* return this.inserters;
* @param id the id of the insertion text
* @param lines the lines to insert
* @return the current {@link ContentHandler} instance
*/
public SELF insert(@NotNull String id, @NotNull String... lines) {
return insert(id, Arrays.asList(lines));
} }
/** @Override
* Insert the specific contents by the id. public SELF inserter(@NotNull ContentInserter<RECEIVER> inserter) {
* this.inserters.add(inserter);
* @param id the id of the insertion text this.inserters.sort(ContentInserter::compareTo);
* @param lines the lines to insert return self();
* @return the current {@link ContentHandler} instance }
*/
public SELF insert(@NotNull String id, @NotNull List<String> lines) { @Override
return insert(id, receiver -> lines); public SELF insert(@NotNull String id, @Nullable Function<RECEIVER, List<String>> supplier) {
this.insertion.put(id, supplier);
return self();
}
@Override
public boolean inserting(@NotNull String id) {
return this.insertion.keySet().stream().anyMatch(key -> key.equalsIgnoreCase(id));
}
@Override
public @Nullable List<String> getInsertion(@NotNull String id, @Nullable RECEIVER receiver) {
Function<RECEIVER, List<String>> function = this.insertion.get(id);
if (function == null) return null;
return function.apply(receiver);
}
@Override
public SELF removeInsert(@NotNull String id) {
this.insertion.remove(id);
return self();
} }
/** /**
@@ -219,63 +216,41 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
* @return the parsed text * @return the parsed text
*/ */
protected @Nullable String parse(@Nullable RECEIVER receiver, @NotNull String text) { protected @Nullable String parse(@Nullable RECEIVER receiver, @NotNull String text) {
return this.parser.apply(receiver, setPlaceholders(text, this.placeholders)); text = applyReplacements(receiver, text); // First, apply the custom replacements
text = this.paramReplacer.replace(text, receiver); // Then, apply the static placeholders
return this.parser.apply(receiver, text); // Finally, parse the text
} }
public void handle(@NotNull TextContents contents, @Nullable RECEIVER receiver, public void handle(@NotNull TextContents contents, @Nullable RECEIVER receiver,
@NotNull Consumer<String> lineConsumer) { @NotNull Consumer<String> lineConsumer) {
if (contents.isEmpty()) return; // Nothing to parse if (contents.isEmpty()) return; // Nothing to parse
if (this.disableInsertion) { if (this.disableInsertion || noneInsertion()) {
contents.lines().forEach(line -> lineConsumer.accept(parse(receiver, line))); contents.lines().forEach(line -> lineConsumer.accept(parse(receiver, line)));
return; // Simple parsed return; // Simple parsed
} }
// Set the default insertion of the text
for (Map.Entry<String, @Nullable Function<RECEIVER, List<String>>> entry : this.insertion.entrySet()) {
if (entry.getValue() != null) continue;
List<String> lines = contents.optionalLines().get(entry.getKey());
if (lines == null) continue;
entry.setValue(r -> lines);
}
lines:
for (String line : contents.lines()) { for (String line : contents.lines()) {
Matcher insertMatcher = INSERT_PATTERN.matcher(line); for (ContentInserter<RECEIVER> inserter : inserters) {
if (insertMatcher.matches()) { List<String> inserted = inserter.handle(receiver, line, this);
doInsert(insertMatcher, receiver, lineConsumer); if (inserted != null) { // Found the insertion
continue; inserted.forEach(l -> lineConsumer.accept(parse(receiver, l)));
continue lines; // Go to the next line
} }
Matcher enableMatcher = ENABLE_PATTERN.matcher(line);
if (enableMatcher.matches()) {
if (this.insertion.containsKey(enableMatcher.group("id"))) {
lineConsumer.accept(parse(receiver, enableMatcher.group("content")));
} }
continue;
}
lineConsumer.accept(parse(receiver, line)); lineConsumer.accept(parse(receiver, line));
} }
} }
private void doInsert(Matcher matcher, @Nullable RECEIVER receiver,
@NotNull Consumer<String> lineConsumer) {
String id = matcher.group("id");
List<String> values = Optional.ofNullable(this.insertion.get(id))
.map(f -> f.apply(receiver))
.orElse(null);
if (values == null || values.isEmpty()) return; // No values to insert
String prefix = matcher.group("prefix");
String type = matcher.group("type");
boolean original = type.equals("@");
int offsetAbove = Optional.ofNullable(matcher.group("above"))
.map(Integer::parseInt).orElse(0);
int offsetDown = Optional.ofNullable(matcher.group("down"))
.map(Integer::parseInt).orElse(offsetAbove); // If offsetDown is not set, use offsetAbove
IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(lineConsumer);
String prefixContent = Optional.ofNullable(prefix).map(p -> parse(receiver, p)).orElse("");
if (original) {
values.stream().map(value -> prefixContent + value).forEach(lineConsumer);
} else {
values.stream().map(value -> prefixContent + parse(receiver, value)).forEach(lineConsumer);
}
IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(lineConsumer);
}
public static String setPlaceholders(@NotNull String messages, public static String setPlaceholders(@NotNull String messages,
@NotNull Map<String, Object> placeholders) { @NotNull Map<String, Object> placeholders) {
if (messages.isEmpty()) return messages; if (messages.isEmpty()) return messages;
@@ -1,6 +1,5 @@
package cc.carm.lib.configuration.value.text.function; package cc.carm.lib.configuration.value.text.function;
import cc.carm.lib.configuration.value.text.data.TextContents;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays; import java.util.Arrays;
@@ -0,0 +1,67 @@
package cc.carm.lib.configuration.value.text.function.common;
import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter;
import cc.carm.lib.configuration.value.text.function.inserter.Insertable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
public class AppendLineInserter<RECEIVER> extends ContentInserter<RECEIVER> {
/**
* Used to match the message insertion.
* <p>
* format:
* <br>- to insert parsed line {prefix}#content-id#{offset-above,offset-down}
* <br>- to insert original line {prefix}@content-id@{offset-above,offset-down}
* <br> original lines will not be parsed
* <br> example:
* <ul>
* <li>{- }#content-id#{1,1}</li>
* <li>@content-id@{1,1}</li>
* </ul>
*/
public static final @NotNull Pattern APPEND_PATTERN = Pattern.compile(
"^(?:\\{(?<prefix>.*)})?#(?<id>.*)#(?:\\{(?<above>-?\\d+)(?:,(?<down>-?\\d+))?})?$"
);
public AppendLineInserter(int priority) {
super(priority, APPEND_PATTERN);
}
@Override
protected @Nullable String extractID(@NotNull Matcher matcher) {
return matcher.group("id");
}
@Override
protected @NotNull List<String> get(@Nullable RECEIVER receiver, @NotNull Matcher matcher,
@NotNull Insertable<RECEIVER, ?> insertion) {
String id = extractID(matcher);
List<String> values = insertion.getInsertion(id, receiver);
if (values == null || values.isEmpty()) return Collections.emptyList(); // No values to insert
String prefix = Optional.ofNullable(matcher.group("prefix")).orElse("");
int offsetAbove = Optional.ofNullable(matcher.group("above"))
.map(Integer::parseInt).orElse(0);
int offsetDown = Optional.ofNullable(matcher.group("down"))
.map(Integer::parseInt).orElse(offsetAbove); // If offsetDown is not set, use offsetAbove
List<String> contents = new ArrayList<>();
IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(contents::add);
values.stream().map(value -> prefix + value).forEach(contents::add);
IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(contents::add);
return contents;
}
}
@@ -0,0 +1,46 @@
package cc.carm.lib.configuration.value.text.function.common;
import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter;
import cc.carm.lib.configuration.value.text.function.inserter.Insertable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class OptionalLineInserter<RECEIVER> extends ContentInserter<RECEIVER> {
/**
* Used to match the message which can be inserted
* <p>
* format:
* <br>- ?[id]Message content
* <br> example:
* <ul>
* <li>?[click]Click to use this item!</li>
* </ul>
*/
public static final @NotNull Pattern OPTIONAL_PATTERN = Pattern.compile(
"^\\?\\[(?<id>.+)](?<content>.*)$"
);
public OptionalLineInserter(int priority) {
super(priority, OPTIONAL_PATTERN);
}
@Override
protected @Nullable String extractID(@NotNull Matcher matcher) {
return matcher.group("id");
}
@Override
protected @NotNull List<String> get(@Nullable RECEIVER receiver, @NotNull Matcher matcher,
@NotNull Insertable<RECEIVER, ?> insertion) {
String content = matcher.group("content");
if (content == null) return Collections.emptyList();
return Collections.singletonList(content);
}
}
@@ -0,0 +1,71 @@
package cc.carm.lib.configuration.value.text.function.common;
import cc.carm.lib.configuration.value.text.function.replacer.ContentReplacer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ParamReplacer<RECEIVER> extends ContentReplacer<RECEIVER> {
public static final @NotNull Pattern DEFAULT_PARAM_PATTERN = Pattern.compile("%\\((?<id>[^)]+)\\)");
public static <R> ParamReplacer<R> defaults() {
return create(DEFAULT_PARAM_PATTERN, m -> m.group("id"));
}
public static <R> ParamReplacer<R> create(@NotNull Pattern pattern, @NotNull Function<Matcher, String> extractor) {
return new ParamReplacer<>(0, pattern, extractor);
}
protected final @NotNull Function<Matcher, @Nullable String> extractor;
protected @NotNull Map<String, Object> placeholders = new HashMap<>();
public ParamReplacer(int priority, @NotNull Pattern pattern, @NotNull Function<Matcher, String> extractor) {
super(priority, pattern);
this.extractor = extractor;
}
public @NotNull Map<String, Object> placeholders() {
return placeholders;
}
@Override
protected @Nullable String get(@NotNull RECEIVER receiver, @NotNull Matcher matcher) {
@Nullable String id = extractor.apply(matcher);
if (id == null) return null;
Object value = placeholders.get(id);
if (value == null) return null;
return value.toString();
}
public void set(@NotNull Map<String, Object> placeholders) {
this.placeholders = placeholders;
}
public void put(@NotNull String id, @Nullable Object value) {
placeholders.put(id, value);
}
public void remove(@NotNull String id) {
placeholders.remove(id);
}
public void putAll(@NotNull Map<String, Object> placeholders) {
this.placeholders.putAll(placeholders);
}
public void putAll(@NotNull String[] params, @NotNull Object[] values) {
for (int i = 0; i < params.length; i++) {
placeholders.put(params[i], (values != null && values.length > i) ? values[i] : "?");
}
}
public void clear() {
placeholders.clear();
}
}
@@ -0,0 +1,51 @@
package cc.carm.lib.configuration.value.text.function.inserter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class ContentInserter<RECEIVER> implements Comparable<ContentInserter<RECEIVER>> {
protected final int priority;
protected @NotNull Pattern pattern;
public ContentInserter(int priority, @NotNull Pattern pattern) {
this.priority = priority;
this.pattern = pattern;
}
public int priority() {
return priority;
}
public void setPattern(@NotNull Pattern pattern) {
this.pattern = pattern;
}
public @NotNull Matcher matcher(@NotNull String text) {
return pattern.matcher(text);
}
protected abstract @Nullable String extractID(@NotNull Matcher matcher);
protected abstract @NotNull List<String> get(@Nullable RECEIVER receiver, @NotNull Matcher matcher,
@NotNull Insertable<RECEIVER, ?> insertions);
public @Nullable List<String> handle(@Nullable RECEIVER receiver, @NotNull String line,
@NotNull Insertable<RECEIVER, ?> insertions) {
Matcher matcher = matcher(line);
if (!matcher.matches()) return null;
if (!insertions.inserting(extractID(matcher))) return Collections.emptyList();
return get(receiver, matcher, insertions);
}
@Override
public int compareTo(@NotNull ContentInserter<RECEIVER> o) {
return Integer.compare(o.priority, priority);
}
}
@@ -0,0 +1,66 @@
package cc.carm.lib.configuration.value.text.function.inserter;
import cc.carm.lib.configuration.value.text.function.ContentHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public interface Insertable<RECEIVER, SELF> {
List<ContentInserter<RECEIVER>> inserters();
SELF inserter(@NotNull ContentInserter<RECEIVER> inserter);
boolean noneInsertion();
boolean inserting(@NotNull String id);
@Nullable List<String> getInsertion(@NotNull String id, @Nullable RECEIVER receiver);
/**
* Insert the specific contents by the id.
*
* @param id the id of the insertion text
* @param supplier to supply the lines to insert
* @return the current {@link ContentHandler} instance
*/
SELF insert(@NotNull String id, @Nullable Function<RECEIVER, List<String>> supplier);
/**
* Insert the specific contents by the id.
*
* @param id the id of the insertion text
* @return the current {@link ContentHandler} instance
*/
default SELF insert(@NotNull String id) {
return insert(id, (Function<RECEIVER, List<String>>) null);
}
/**
* Insert the specific contents by the id.
*
* @param id the id of the insertion text
* @param contents the lines to insert
* @return the current {@link ContentHandler} instance
*/
default SELF insert(@NotNull String id, @NotNull List<String> contents) {
return insert(id, receiver -> contents);
}
/**
* Insert the specific contents by the id.
*
* @param id the id of the insertion text
* @param contents the lines to insert
* @return the current {@link ContentHandler} instance
*/
default SELF insert(@NotNull String id, @NotNull String... contents) {
return insert(id, Arrays.asList(contents));
}
SELF removeInsert(@NotNull String id);
}
@@ -0,0 +1,105 @@
package cc.carm.lib.configuration.value.text.function.replacer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class ContentReplacer<RECEIVER> implements Comparable<ContentReplacer<RECEIVER>> {
public static <R> ContentReplacer.Builder<R> match(@NotNull Pattern pattern) {
return new Builder<>(pattern);
}
public static <R> ContentReplacer.Builder<R> match(@NotNull String pattern) {
return match(Pattern.compile(pattern));
}
public static <R> ContentReplacer.Builder<R> replace(@NotNull String text) {
return match(Pattern.quote(text));
}
protected final int priority;
protected @NotNull Pattern pattern;
public ContentReplacer(int priority, @NotNull Pattern pattern) {
this.priority = priority;
this.pattern = pattern;
}
public int priority() {
return priority;
}
public void pattern(@NotNull Pattern pattern) {
this.pattern = pattern;
}
public @NotNull Matcher matcher(@NotNull String text) {
return pattern.matcher(text);
}
public String replace(@NotNull String text, @Nullable RECEIVER receiver) {
Matcher matcher = matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String replaced = get(receiver, matcher);
matcher.appendReplacement(sb, replaced == null ? "" : replaced);
}
matcher.appendTail(sb);
return sb.toString();
}
protected abstract @Nullable String get(@Nullable RECEIVER receiver, @NotNull Matcher matcher);
@Override
public int compareTo(@NotNull ContentReplacer<RECEIVER> o) {
return Integer.compare(o.priority, this.priority);
}
public static class Builder<R> {
protected final @NotNull Pattern pattern;
protected int priority = 0;
public Builder(@NotNull Pattern pattern) {
this.pattern = pattern;
}
public @NotNull Builder<R> priority(int priority) {
this.priority = priority;
return this;
}
public @NotNull ContentReplacer<R> to(BiFunction<@Nullable R, @NotNull Matcher, @Nullable String> supplier) {
return new ContentReplacer<R>(this.priority, this.pattern) {
@Override
protected @Nullable String get(@NotNull R receiver, @NotNull Matcher matcher) {
return supplier.apply(receiver, matcher);
}
};
}
public @NotNull ContentReplacer<R> to(Function<@Nullable R, @Nullable String> supplier) {
return to((receiver, matchedParam) -> supplier.apply(receiver));
}
public @NotNull ContentReplacer<R> to(Supplier<@Nullable String> supplier) {
return to((receiver, matchedParam) -> supplier.get());
}
public @NotNull ContentReplacer<R> to(@NotNull String content) {
return to(() -> content);
}
}
@FunctionalInterface
public interface Constructor<R> extends Function<ContentReplacer.Builder<R>, ContentReplacer<R>> {
}
}
@@ -0,0 +1,63 @@
package cc.carm.lib.configuration.value.text.function.replacer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public interface Replaceable<RECEIVER, SELF> {
List<ContentReplacer<RECEIVER>> replacers();
default String applyReplacements(@Nullable RECEIVER receiver, @NotNull String text) {
for (ContentReplacer<RECEIVER> replacer : replacers()) {
text = replacer.replace(text, receiver);
}
return text;
}
SELF replacer(@NotNull ContentReplacer<RECEIVER> replacer);
default SELF replacer(@NotNull Pattern pattern, @NotNull ContentReplacer.Constructor<RECEIVER> constructor) {
ContentReplacer.Builder<RECEIVER> builder = ContentReplacer.match(pattern);
return replacer(constructor.apply(builder));
}
default SELF replace(@NotNull Pattern pattern,
@NotNull BiFunction<@Nullable RECEIVER, @NotNull Matcher, @Nullable String> supplier) {
return replacer(pattern, builder -> builder.to(supplier));
}
default SELF replace(@NotNull Pattern pattern,
@NotNull Function<@Nullable RECEIVER, @Nullable String> supplier) {
return replacer(pattern, builder -> builder.to(supplier));
}
default SELF replace(@NotNull Pattern pattern,
@NotNull Supplier<@Nullable String> supplier) {
return replacer(pattern, builder -> builder.to(supplier));
}
default SELF replace(@NotNull Pattern pattern, @NotNull String replacement) {
return replacer(pattern, builder -> builder.to(replacement));
}
default SELF replace(@NotNull String text, @NotNull String replacement) {
return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(replacement));
}
default SELF replace(@NotNull String text, @NotNull Supplier<@Nullable String> supplier) {
return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(supplier));
}
default SELF replace(@NotNull String text,
@NotNull Function<@Nullable RECEIVER, @Nullable String> supplier) {
return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(supplier));
}
}
@@ -7,17 +7,16 @@ import cc.carm.lib.configuration.value.text.tests.conf.AppMessages;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.util.List;
public class ConfigTest { public class ConfigTest {
public static final String[] WEBSITES = new String[]{ public static final String[] WEBSITES = new String[]{
"https://carm.cc", "https://carm.cc",
"https://www.baidu.com", "https://www.baidu.com",
"https://www.google.com" "https://www.google.com"
}; };
@Test @Test
public void test() { public void test() {
@@ -27,11 +26,11 @@ public class ConfigTest {
System.out.println("--------------------------"); System.out.println("--------------------------");
AppMessages.WELCOME.prepare() List<String> str = AppMessages.WELCOME.prepare()
.placeholders("Carm") .placeholders("Carm")
.insert("guidance") .insert("guidance")
.insert("websites", WEBSITES) .insert("websites", WEBSITES)
.to(System.out); .compile(System.out);
System.out.println("--------------------------"); System.out.println("--------------------------");
@@ -7,6 +7,7 @@ import org.junit.Test;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.*; import java.util.*;
public class ParseTest { public class ParseTest {
@@ -18,14 +19,15 @@ public class ParseTest {
lines.add("#more-creating#{1}"); lines.add("#more-creating#{1}");
lines.add("This is a test message"); lines.add("This is a test message");
lines.add("#guidance#"); lines.add("#guidance#");
lines.add("{- }#websites#{0,1}"); lines.add("{ - }#websites#{0,1}");
lines.add("Thanks for your reading!"); lines.add("Thanks for your reading!");
lines.add("uuid: $UUID$");
lines.add("?[click]"); lines.add("?[click]");
lines.add("?[click]Click to see more!"); lines.add("?[click]Click to see more!");
lines.add("?[hidden]This entry should be hidden!"); lines.add("?[hidden]This entry should be hidden!");
Map<String, List<String>> optional = new HashMap<>(); Map<String, List<String>> optional = new HashMap<>();
optional.put("guidance", Arrays.asList("To get more information for %(name), see:")); optional.put("guidance", Arrays.asList("To get more information for %(name),", " see:"));
optional.put("websites", Arrays.asList("https://www.baidu.com", "https://www.google.com")); optional.put("websites", Arrays.asList("https://www.baidu.com", "https://www.google.com"));
TextContents textContents = new TextContents(lines, optional); TextContents textContents = new TextContents(lines, optional);
@@ -33,13 +35,17 @@ public class ParseTest {
PreparedText<String, PrintStream> msg = new PreparedText<String, PrintStream>(textContents) PreparedText<String, PrintStream> msg = new PreparedText<String, PrintStream>(textContents)
.dispatcher((p, s) -> s.forEach(p::println)) .dispatcher((p, s) -> s.forEach(p::println))
.parser((p, s) -> s) .parser((p, s) -> s)
.compiler((p, s) -> s); .compiler((p, s) -> s)
.replace( // Custom replacer, replace $UUID$ with Random UUID
"$UUID$", () -> UUID.randomUUID().toString()
);
msg.placeholder("name", "Carm") msg.placeholder("name", "Carm")
.insert("guidance") .insert("guidance")
.insert("click") .insert("click")
.insert("websites", "Baidu", "Bilibili", "Google"); .insert("websites", "Baidu", "Bilibili", "Google");
System.out.println("----------------------------"); System.out.println("----------------------------");
msg.to(System.out); msg.to(System.out);
System.out.println("----------------------------"); System.out.println("----------------------------");
@@ -17,7 +17,8 @@ public class ConfiguredMsg extends ConfiguredText<String, PrintStream> {
return builder().defaults(text).build(); return builder().defaults(text).build();
} }
public ConfiguredMsg(@NotNull ValueManifest<TextContents> manifest, @NotNull String[] params) { public ConfiguredMsg(@NotNull ValueManifest<TextContents, TextContents> manifest,
@NotNull String[] params) {
super( super(
manifest, manifest,
(p, s) -> s, (p, s) -> s,
+51
View File
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId>
<version>4.1.7</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
<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>configured-feature-validators</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>configured-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,16 @@
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;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValuePattern {
String value();
String message() default "Invalid value format!";
}
@@ -0,0 +1,18 @@
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;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValueRange {
long min() default Long.MIN_VALUE;
long max() default Long.MAX_VALUE;
String message() default "Value out of range.";
}
@@ -0,0 +1,44 @@
package cc.carm.lib.configuration.validators;
import cc.carm.lib.configuration.annotation.ValuePattern;
import cc.carm.lib.configuration.annotation.ValueRange;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
public class Validators {
private Validators() {
throw new UnsupportedOperationException("API Register.");
}
public static void activate(ConfigurationHolder<?> holder) {
holder.initializer().registerValidAnnotation(ValueRange.class, r -> (ho, value) -> {
if (!(value instanceof Number)) {
throw new IllegalArgumentException("Value is not a number: " + value);
}
Number number = (Number) value;
if (number.doubleValue() < r.min() || number.doubleValue() > r.max()) {
throw new IllegalArgumentException(r.message());
}
});
holder.initializer().registerValidAnnotation(ValuePattern.class, r -> new ValueValidator<Object>() {
private final Pattern pattern = Pattern.compile(r.value());
@Override
public void validate(@NotNull ConfigurationHolder<?> holder, @Nullable Object value) throws Exception {
if (value == null) return;
if (!pattern.matcher(value.toString()).matches()) {
throw new IllegalArgumentException(r.message());
}
}
});
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+22 -16
View File
@@ -5,25 +5,30 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
<project.jdk.version>1.8</project.jdk.version> <project.jdk.version>8</project.jdk.version>
<maven.compiler.source>${project.jdk.version}</maven.compiler.source> <maven.compiler.source>${project.jdk.version}</maven.compiler.source>
<maven.compiler.target>${project.jdk.version}</maven.compiler.target> <maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<kotlin.version>2.2.0</kotlin.version>
</properties> </properties>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>4.1.0</version> <version>4.1.7</version>
<modules> <modules>
<module>core</module> <module>core</module>
<module>features/section</module> <module>features/section</module>
<module>features/file</module> <module>features/file</module>
<module>features/commentable</module> <module>features/commentable</module>
<module>features/versioned</module> <module>features/versioned</module>
<module>features/validators</module>
<module>features/text</module> <module>features/text</module>
<module>features/kotlin</module>
<module>features/record</module>
<module>providers/temp</module>
<module>providers/yaml</module> <module>providers/yaml</module>
<module>providers/gson</module> <module>providers/gson</module>
<module>providers/hocon</module> <module>providers/hocon</module>
@@ -73,22 +78,21 @@
<repositories> <repositories>
<repository>
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
<repository>
<id>nexus</id>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository> <repository>
<id>central</id> <id>central</id>
<url>https://repo1.maven.org/maven2/</url> <url>https://repo1.maven.org/maven2/</url>
</repository> </repository>
<repository>
<id>sonatype</id>
<url>https://s01.oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
@@ -125,7 +129,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version> <version>3.5.3</version>
<configuration> <configuration>
<useSystemClassLoader>false</useSystemClassLoader> <useSystemClassLoader>false</useSystemClassLoader>
</configuration> </configuration>
@@ -165,7 +169,6 @@
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
@@ -275,11 +278,14 @@
<plugin> <plugin>
<groupId>org.sonatype.central</groupId> <groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId> <artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version> <version>0.8.0</version>
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<publishingServerId>central</publishingServerId> <publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish> <autoPublish>true</autoPublish>
<excludeArtifacts>
<excludeArtifact>configured-demo</excludeArtifact>
</excludeArtifacts>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
+18 -2
View File
@@ -1,8 +1,24 @@
# configured-JSON # configured-JSON
JSON file-based implementation, compatible with all Java environments. JSON
file-based
implementation,
compatible
with
all
Java
environments.
**Remember that JSON does not support file comments.** *
*Remember
that
JSON
does
not
support
file
comments.
**
## Dependencies ## Dependencies
+2 -9
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>configured-parent</artifactId> <artifactId>configured-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.1.0</version> <version>4.1.7</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -41,17 +41,10 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-demo</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.12.1</version> <version>2.13.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>

Some files were not shown because too many files have changed in this diff Show More