Compare commits

...

113 Commits

Author SHA1 Message Date
ab7259f726 [RELEASE] create version v0.13.0 2024-06-25 15:47:53 +02:00
cdb4581799 [FEAT] add link of List<UUID> that is decorate with @ManyToOne, or @ManyToMany or @OneToMany 2024-06-20 00:10:22 +02:00
7e81bfef28 [FEAT] add generation of dot files 2024-06-20 00:10:22 +02:00
84525fd7aa [FIX] cover tools 2024-06-13 08:27:33 +02:00
d4eb9c2a5f [FIX] correct the ID in uuid for uuid primary-key 2024-06-12 20:08:26 +02:00
15688f93e5 [FIX] version number 2024-06-12 00:55:51 +02:00
906216f237 [FIX] throw when uploading data 2024-06-12 00:54:42 +02:00
b479414bc2 [FIX] callbacks elements 2024-06-12 00:54:22 +02:00
2bc68321e3 [DEV] develoopment version 2024-06-12 00:53:29 +02:00
d77a5518ff [RELEASE] create version v0.12.1 2024-06-11 23:59:59 +02:00
5f9d13d315 [FEAT] remove in upload cover the filename 2024-06-11 23:48:43 +02:00
Yoann Fleury
0b57d8571c fix: plural for callbacks 2024-06-11 12:41:31 +02:00
483c41914a [RELEASE] create version v0.12.0 2024-06-10 22:26:57 +02:00
a1f56050bf [FEAT] support the min and Max for number and string 2024-06-08 13:48:34 +02:00
a41e837f21 [FIX] import "Write" when no write availlable. 2024-06-08 11:55:51 +02:00
5496855698 [FEAT] add a remove warning 2024-06-08 11:43:29 +02:00
c9cb0d043a [API] remove @SQLWhere 2024-06-08 11:43:00 +02:00
09cfcfc578 [FEAT] generate a full Zod object for write mode.
- Add @NoWriteSpecificMode to permit to remove specific object write model
  - refactor Zod Write model
  - Add .nullable() in write Optional element

Residual bug element use in APi that is mark as no write
2024-06-08 11:42:38 +02:00
e4c56a4da5 [RELEASE] create version v0.11.0 2024-06-02 21:30:58 +02:00
22614aee98 [DEPENDENCY] update dependency 2024-06-02 21:30:36 +02:00
5bbcf63c42 [FIX] close some statements 2024-06-02 21:28:29 +02:00
1c02d333a3 [FIX] remove a warnning of spotbug 2024-06-02 21:28:12 +02:00
1c41ea4273 [FEAT] add a simple tool to generate random string (usefull in test) 2024-06-02 21:27:53 +02:00
274767d89b [FEAT] add some Logs when fail 2024-06-02 21:27:20 +02:00
4236dc38bd [FIX] correct many data-access element that does not close elements 2024-06-02 21:23:57 +02:00
7d4b246d4a [FEAT] add dependency of spotbug to have better code 2024-06-02 21:23:26 +02:00
f6f256c006 [FIX] Correct Exception types in the DataJson Add-on 2024-06-02 13:11:25 +02:00
d46b84c741 [FEAT] support DELETE return Data 2024-06-02 13:10:38 +02:00
3e6b9bf77c [FIX] Correct RESTApi.gets retreive data list in a correct way 2024-06-02 13:10:18 +02:00
007003394a [FEAT] add FailException adding exception context to permit better display of error in the future 2024-06-02 13:09:30 +02:00
54d4c420f9 [FEAT] DataAccess reduce log levels 2024-06-02 13:08:48 +02:00
0a307f3f6e [FIX] correct annotation tool throws 2024-06-02 13:08:21 +02:00
d968a2c48f [DOC] add spotbug documentation 2024-06-02 13:07:51 +02:00
c43e283b57 [FIX] socme checks 2024-06-01 19:49:36 +02:00
6af6f91166 [FIX] SpotBug finding some errors 2024-06-01 16:48:59 +02:00
c94f488747 [FIX] better compatibility with @OneToMany 2024-06-01 13:53:48 +02:00
c44b726cc1 [FEAT] add test for @OneToMany that fail 2024-06-01 13:10:28 +02:00
c412daa1ca [TEST] simplify test model 2024-06-01 13:09:53 +02:00
aa700f9dc5 [FEAT] add a throw in an impossible class call 2024-06-01 13:09:20 +02:00
a1791cf61d [TEST] LOG in TRACE only the library 2024-06-01 13:08:45 +02:00
9c9da21bdb [STYLE] error commit 2024-06-01 13:08:15 +02:00
dc6eeac008 [FIX] fix @ManyToOne (add UUID test) 2024-06-01 13:07:58 +02:00
f3a9ebf5e1 [STYLE] set all the typescript generation in a sub-folder 2024-06-01 13:01:32 +02:00
1fe3cc3523 [FIX] Correct the fail retur API to transmit the error from backend when compatible and wrat it when error occured 2024-05-31 21:50:50 +02:00
91849094cd [DEV] Correct all catcher to be named normalized 2024-05-31 19:51:24 +02:00
7b72b08fc0 [FEAT] permit a better support of @OneToMany 2024-05-29 20:16:48 +02:00
ebf1b4b76a [RELEASE] create version v0.10.3 2024-05-29 10:40:06 +02:00
f6aff28488 [FIX] ser cover nullable 2024-05-29 00:01:28 +02:00
6ebb9eb395 [FIX] remove accept type when void return 2024-05-29 00:01:04 +02:00
17664f1c2f [FIX] missing delete in links 2024-05-29 00:00:08 +02:00
1373760498 [FIX] correct the declaration of Object 2024-05-28 22:22:24 +02:00
8ffa392b2a [FEAT] create sub-directories 2024-05-28 22:21:05 +02:00
6104b68a02 [FIX] correct input order. 2024-05-28 22:07:41 +02:00
3c85af5af9 [FEAT] add a contructor to simplify a checker API 2024-05-28 18:04:54 +02:00
38d1fa9241 [DEV] simple rework the Check JPA 2024-05-28 18:04:31 +02:00
0e498c6a26 [RELEASE] create version v0.10.2 2024-05-27 17:00:52 +02:00
187ffba188 [FEAT] manage @nullable contrainst on list and Map too. 2024-05-27 17:00:31 +02:00
2a2b6b8f3b [RELEASE] create version v0.10.1 2024-05-24 12:33:16 +02:00
73770b03c3 [FIX] wrong type when set the @AsyncModel 2024-05-24 12:32:16 +02:00
5d8d4d9d2a [RELEASE] create version v0.10.0 2024-05-24 00:27:50 +02:00
745b0cb39b [FIX] correct API generation 2024-05-24 00:23:11 +02:00
9bad883866 [FEAT] add capability to support null queryOption to simplify API 2024-05-23 23:03:54 +02:00
f1c3b88a00 [DEV] add checker option to permit to inject some property in the checker 2024-05-23 00:08:56 +02:00
8c30336bdb [FIX] Correct Rest tool interface for stric mode of Typescript 2024-05-22 17:27:36 +02:00
6f36f98cd6 [FIX] correct the JPA display of minimum error 2024-05-22 16:29:49 +02:00
c843d7a64b [FIX] correct the size of ErrorRespose and remove needed of UUID 2024-05-22 16:29:21 +02:00
7059e687db [RELEASE] create version v0.9.0 2024-05-21 23:54:32 +02:00
bfb6498782 [FIX] remove dead test 2024-05-21 23:54:01 +02:00
94abf842e7 [DEV] update dependency 2024-05-21 23:54:01 +02:00
300ce8eab6 [FIX] correct test 2024-05-21 23:54:01 +02:00
c33a73a567 [STYLE] format code 2024-05-21 23:54:01 +02:00
0597ba0df3 [FEAT remove old API and add type in enum int model 2024-05-21 23:53:57 +02:00
4dabfcf7d7 [DEV] full rework of Typescript generator (operational) 2024-05-21 23:53:31 +02:00
4f7d2d711f [DEV] first step to generation of API 2024-05-21 23:53:31 +02:00
9ac3a95060 [FEAT] model generation is ready 2024-05-21 23:53:31 +02:00
dc022abd2d [FEAT] continue refacto of the model generator (base model generated, not the API, mission some control like optional, and limit size 2024-05-21 23:53:26 +02:00
7a36580cce [FEAT] continue refacto of the generation code 2024-05-21 23:52:53 +02:00
6031e6e557 [FEAT] continue integration 2024-05-21 23:52:53 +02:00
6d6fbf93ca [DEV] refacto the external rest api generator 2024-05-21 23:52:53 +02:00
e1f1335e89 [DEV] update version dev 2024-05-20 13:19:59 +02:00
e914daf71b [RELEASE] release v0.8.11 2024-05-20 13:19:31 +02:00
d859c7c278 [FIX] update any lines 2024-05-20 13:18:43 +02:00
420ea94b42 [DEV] update development tag 2024-05-20 09:59:21 +02:00
604c76103f [RELEASE] create version v0.8.10 2024-05-20 09:55:54 +02:00
1fbc9e5874 [API] remove api to generate zod models index to prevent coding errors 2024-05-16 18:53:34 +02:00
17fdd7b83b [FIX] correct enum type converting 2024-05-16 18:52:11 +02:00
4cbad1fc6a [FIX] correct the deleted mode in @ManyToMany 2024-05-16 18:51:48 +02:00
6e784f5f50 Conver user cover as json and link table with UUID and delete 2024-05-15 23:24:04 +02:00
81672115c8 [RELEASE] release version v0.8.9 2024-05-12 23:55:19 +02:00
25e6972efd [FIX] error of .ts 2024-05-12 23:54:08 +02:00
850964932f [RELEASE] release version v0.8.8 2024-05-12 23:41:48 +02:00
b3364753dd [FEAT] correct .ts in include 2024-05-12 23:38:52 +02:00
a0d96f1322 [RELEASA] create v0.8.7 2024-05-12 09:06:29 +02:00
ad3c1f4b7d [FIX] missing basic constructor 2024-05-12 09:05:45 +02:00
a77db1035d [DEPEN] update dependency 2024-05-12 08:40:18 +02:00
f1a31fed56 [FIX] permit to not check the type getted 2024-05-09 00:12:42 +02:00
7c087fcd8f [FIX] correct the void get values 2024-05-09 00:11:14 +02:00
183906dc97 [FIX] correct format of json timestamp 2024-05-09 00:10:46 +02:00
94e5603380 [FIX] correct zod model for dates and times 2024-05-09 00:10:04 +02:00
f06b5a1edc [DEV] set code compatible with ES2018 2024-05-08 19:11:04 +02:00
f3f6cd91d0 [RELEASE] release version v0.8.6 2024-05-06 21:52:05 +02:00
6c048ac22b [FEAT] generate the typescipt interface ordered 2024-05-06 21:50:38 +02:00
755b258ddb [RELEASE] create version v0.8.5 2024-05-06 19:08:19 +02:00
4be9f663ac [DOC] add some documentation 2024-05-05 12:33:49 +02:00
280d86e333 [FEAT] add a minimum sample (Not tested) 2024-05-05 12:33:33 +02:00
70cbb1aae9 [FEAT] reorganize pakages 2024-05-05 12:33:08 +02:00
9d5c3c01db [DEV] correct input order 2024-05-04 10:05:16 +02:00
148bdcbabd [RELEASE] new version v0.8.4 2024-05-03 12:12:05 +02:00
46d1284174 [FEAT] update well produce when woid is return 2024-05-01 23:10:27 +02:00
866e28a645 [RELEASE] new version v0.8.3 2024-04-30 21:58:56 +02:00
62ec396ff7 [FEAT] Add capability to merge condition option in request and correct some back for SQLITE 2024-04-30 21:57:58 +02:00
4517854749 [DEV] update th format of the code 2024-04-28 16:56:33 +02:00
29a6e7602c [FEAT] add capability to set condition in update 2024-04-28 11:44:01 +02:00
149 changed files with 8092 additions and 2780 deletions

View File

@@ -1,4 +1,14 @@
#!/bin/bash
version_file="version.txt"
mvn versions:set -DnewVersion=$(cat version.txt)
# update the Maven version number
mvn versions:set -DnewVersion=$(sed 's/dev/SNAPSHOT/g' $version_file)
if grep -q "DEV" "$version_file"; then
# update all versions release of dependency
mvn versions:use-latest-releases
# update our manage dependency as snapshoot
mvn versions:use-latest-versions -Dincludes=kangaroo-and-rabbit
else
# update our manage dependency as release (must be done before)
mvn versions:use-latest-releases -Dincludes=kangaroo-and-rabbit
fi

View File

@@ -10,6 +10,11 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
@@ -19,6 +24,7 @@
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>edu.umd.cs.findbugs.plugin.eclipse.findbugsNature</nature>
</natures>
<filteredResources>
<filter>

View File

@@ -1,365 +1,400 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE xml>
<profiles version="18">
<profile kind="CodeFormatterProfile" name="Scenarium" version="18">
<profiles version="23">
<profile kind="CodeFormatterProfile" name="EWOL" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="preserve_positions"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="200"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="200"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="33"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="200"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>

View File

@@ -93,7 +93,12 @@ Enable the pre-commit checker
> **_Note_**: You can change the code in `.git/hooks/pre-commit` by replacing `formatter:verify` with `formatter:format` to auto format the code @ every commit
Run Spot-bug:
------------
```bash
mvn spotbugs:check
```
Add Gitea in the dependency for the registry:
=============================================

202
doc/SQL_wrapper.md Normal file
View File

@@ -0,0 +1,202 @@
Wrapping over the DB
====================
In the current version we consider 2 DB access: MySQL and SQLite, We commonly use the SQLite in test mode to permit to test is a real case.
Generic Access:
---------------
Some generic function are available to permit you to simply request the DB
### Create a new Value:
```java
MyDataModel dataToInsert = ...;
MyDataModel dataCreated = DataAccess.insert(dataToInsert);
```
### Get a full table:
```java
List<MyDataModel> data = DataAccess.gets(MyDataModel.class);
```
### Get a single element in the DB:
```java
UUID id = ...;
MyDataModel data = DataAccess.get(MyDataModel.class, id);
```
> **_Note:_** The Id type fully managed are UUID and Long
### Removing the Data:
```java
UUID id = ...;
DataAccess.delete(MyDataModel.class, id);
```
> **_Note:_** Removing the data automatically detect if it is a software remove or definitive remove
### Updating the Data:
The update of the data can be managed by 2 way:
- Direct update of the Object with direct injection (Good for `PUT`)
- Update with input json (Good for `PATCH`)
The second way is preferable for some reasons
- When jakarta transform the data in you object, we can not detect the element set at null or not set (We consider user of `Optional` il all data class will create a too bug amount of unneeded code in all dev parts)
- Throw in the jakarta parsing are not well catch when we will generate generic error
- The Check will permit to explain what is wrong better than a generic Json parser.
Updating with JSON:
```java
UUID id = ...;
String jsonRequest = "{...}";
DataAccess.updateWithJson(MyDataModel.class, id, jsonRequest);
```
Updating with direct data:
```java
UUID id = ...;
MyDataModel dataToUpdate = ...;
// This update all fields:
DataAccess.update(dataToUpdate, id);
// Select the field to update:
DataAccess.update(dataToUpdate, id, List.of("field1","field2"));
```
Generic option of the request:
------------------------------
Many API have a generic multiple option available like:
```java
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOption... option) throws Exception
```
You just need to add your options in the list of `option`.
Filter the list of field read:
```java
public FilterValue(final String... filterValue)
public FilterValue(final List<String> filterValues)
// example:
new newFilterValue("field1", "field2");
```
Add a condition [more detail](#condition-models)
```java
public Condition(final QueryItem items)
```
Order the request:
```java
public OrderBy(final OrderItem... childs);
// example:
new OrderBy(new OrderItem("name", Order.DESC));
```
Limit the :
```java
public Limit(final long limit)
// example:
new Limit(50);
```
Read all column like update date and create date or delete field
```java
public ReadAllColumn()
```
Condition models:
-----------------
Creating a condition independent of the DB model use need to have a little abstraction of the query model:
For this we propose some condition that update with the internal "auto" condition that is added (like the soft delete...)
### default generic comparator
This is the base of the comparison tool. It compare a column with a value
```java
public QueryCondition(final String key, final String comparator, final Object value);
```
Simple DB comparison element. Note the injected object is injected in the statement and not in the query directly.
Example:
```java
String nameToCheck = "plop";
new QueryCondition("fieldName", "=", nameToCheck);
// OR:
UUID uuid = ...;
new QueryCondition("uuid", "=", uuid);
```
### List comparator
It permit to check a column is (NOT) in a list of value
```java
public QueryInList(final String key, final List<T> value)
public QueryInList(final String key, final T... value)
```
and his opposite:
```java
public QueryNotInList(final String key, final List<T> value)
public QueryNotInList(final String key, final T... value)
```
example
```java
new QueryInList("uuid", List.of(uuid1, uuid2));
```
### NULL and NOT NULL checker
This permit to check an element is `NULL` or `not NULL`
```java
public QueryNull(final String key);
public QueryNotNull(final String key);
```
### The group condition:
The generic `OR` group:
```java
public QueryOr(final List<QueryItem> child);
public QueryOr(final QueryItem... child);
```
Or the generic `AND group:
```java
public QueryAnd(final List<QueryItem> child);
public QueryAnd(final QueryItem... child);
```
### Full example:
```java
List<MyDataModel> result = DataAccess.getsWhere(MyDataModel.class,
new Limit(50),
new OrderBy(new OrderItem("name", Order.DESC)),
new Condition(
new QueryAnd(
QueryNull("Field3")
new QueryOr(
new QueryInList("Field4", 5, 55, 65, 62, 27, 84),
new QueryCondition("cityID", ">", 78000);
)
)
)
);
```

View File

@@ -0,0 +1,3 @@
Basic tutorial
==============

16
doc/data_model.md Normal file
View File

@@ -0,0 +1,16 @@
Data Model
==========
The data model have 2 parts: the model of the DB and the model of the API. It is important to consider the 2 part due to the fact annotation will be manage by by a part, ot the other. Here we will study the Checking of the Data.
DB data model
=============
API data model
==============
Checking of the Data
====================

16
doc/index.md Normal file
View File

@@ -0,0 +1,16 @@
Archi-data documentation
========================
Archidata is base on generic Jakarta, grizzly and SQL or SQLite service.
It permit to simplify the interface of the server to concentrate on the API design.
Archi-data is a group of tools that manage some parts:
- [Wrapping over the DB](SQL_wrapper.md)
- [Data Model](data_model.md)
- [TypeScript Api generation](typescript_api_generation.md)
- [Migration tool](migration.md)
Some initialization tutorials:
- [Basic tutorial](TUTORIAL_01_first_install.md)

7
doc/migration.md Normal file
View File

@@ -0,0 +1,7 @@
Migration tool
==============
Migration is a big part of the Server environment, it permit to migrate an old DB to a new one, and some tool permit the opposite.
Archi-data does not permit to do a reverse migration. I implement some code for it, but is was never tested. On my professional activity, we migrate only forward (Request if if really needed).

View File

@@ -0,0 +1,5 @@
TypeScript Api generation
=========================

83
pom.xml
View File

@@ -3,8 +3,9 @@
<modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.8.2</version>
<version>0.13.0</version>
<properties>
<java.version>21</java.version>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
@@ -45,12 +46,12 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<version>2.1.0-alpha1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
@@ -78,12 +79,12 @@
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
@@ -107,52 +108,58 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.16.1</version>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.16.1</version>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M1</version>
<version>6.1.0-M2</version>
<scope>provided</scope>
</dependency>
<!-- Interface for My-sql & sqlite DB -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<version>8.4.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.40.0.0</version>
<version>3.46.0.0</version>
</dependency>
<!-- Interface for JWT token -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.1</version>
<version>9.39.3</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0-M1</version>
<version>3.2.0</version>
</dependency>
<!-- Swagger dependencies -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.1.10</version>
<version>2.2.22</version>
</dependency>
<!-- spotbug tooling -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.8.5</version>
<scope>compile</scope>
</dependency>
<!--
************************************************************
@@ -162,27 +169,26 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.0</version>
</dependency>
</dependencies>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
@@ -268,7 +274,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<version>3.3.1</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
@@ -280,7 +286,7 @@
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<version>2.23.0</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@@ -304,6 +310,23 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->

View File

@@ -0,0 +1,126 @@
package sample.archidata.basic;
import java.net.URI;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.catcher.ExceptionCatcher;
import org.kar.archidata.catcher.FailExceptionCatcher;
import org.kar.archidata.catcher.InputExceptionCatcher;
import org.kar.archidata.catcher.SystemExceptionCatcher;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable;
import sample.archidata.basic.api.Front;
import sample.archidata.basic.api.HealthCheck;
import sample.archidata.basic.api.MediaResource;
import sample.archidata.basic.api.SeasonResource;
import sample.archidata.basic.api.SeriesResource;
import sample.archidata.basic.api.TypeResource;
import sample.archidata.basic.api.UserMediaAdvancementResource;
import sample.archidata.basic.api.UserResource;
import sample.archidata.basic.filter.KarideoAuthenticationFilter;
import sample.archidata.basic.migration.Initialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.UriBuilder;
public class WebLauncher {
final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class);
public static DBConfig dbConfig;
protected UpdateJwtPublicKey keyUpdater = null;
protected HttpServer server = null;
public WebLauncher() {
ConfigBaseVariable.bdDatabase = "sample_archidata_basic";
}
private static URI getBaseURI() {
return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build();
}
public void migrateDB() throws Exception {
WebLauncher.LOGGER.info("Create migration engine");
final MigrationEngine migrationEngine = new MigrationEngine();
WebLauncher.LOGGER.info("Add initialization");
migrationEngine.setInit(new Initialization());
//WebLauncher.LOGGER.info("Add migration since last version");
//migrationEngine.add(new Migration20230810());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig);
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
}
public static void main(final String[] args) throws Exception {
WebLauncher.LOGGER.info("[START] application wake UP");
final WebLauncher launcher = new WebLauncher();
launcher.migrateDB();
launcher.process();
WebLauncher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
WebLauncher.LOGGER.info("STOP the REST server");
}
public void process() throws InterruptedException {
// ===================================================================
// Configure resources
// ===================================================================
final ResourceConfig rc = new ResourceConfig();
// Permit to accept OPTION request
rc.register(OptionFilter.class);
// remove cors ==> not obviously needed ...
rc.register(CORSFilter.class);
// register exception catcher (this permit to format return error with a normalized JSON)
rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
rc.register(ExceptionCatcher.class);
// add default resource:
rc.register(MyModelResource.class);
// add jackson to be discover when we are in stand-alone server
rc.register(JacksonFeature.class);
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
final HttpServer serverLink = this.server;
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Stopping server..");
serverLink.shutdownNow();
}
}, "shutdownHook"));
// ===================================================================
// run JERSEY
// ===================================================================
try {
this.server.start();
LOGGER.info("Jersey app started at {}", getBaseURI());
} catch (final Exception e) {
LOGGER.error("There was an error while starting Grizzly HTTP server.");
e.printStackTrace();
}
}
// This is used for TEST (See it later)
public void stop() {
if (this.server != null) {
this.server.shutdownNow();
this.server = null;
}
}
}

View File

@@ -0,0 +1,45 @@
package sample.archidata.basic;
import java.util.List;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataFactoryTsApi;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebLauncherLocal extends WebLauncher {
private final static Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
private WebLauncherLocal() {}
public static void main(final String[] args) throws Exception {
final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process();
LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
LOGGER.info("STOP the REST server:");
}
@Override
public void process() throws InterruptedException {
if (true) {
// for local test:
ConfigBaseVariable.apiAddress = "http://0.0.0.0:9000/sample/api/";
ConfigBaseVariable.dbPort = "3906";
}
try {
super.migrateDB();
} catch (final Exception e) {
e.printStackTrace();
while (true) {
LOGGER.error("============================================================================");
LOGGER.error("== Migration fail ==> waiting intervention of administrator...");
LOGGER.error("============================================================================");
Thread.sleep(60 * 60 * 1000);
}
}
super.process();
}
}

View File

@@ -0,0 +1,79 @@
package sample.archidata.basic.api;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.exception.InputException;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.DataTools;
import sample.archidata.basic.model.MyModel;
import sample.archidata.basic.model.Season;
import sample.archidata.basic.model.Series;
import sample.archidata.basic.model.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MyModelType;
@Path("/media")
@Produces(MyModelType.APPLICATION_JSON)
public class MyModelResource {
@GET
@PermitAll
public List<MyModel> gets() throws Exception {
return DataAccess.gets(MyModel.class);
}
@POST
@PermitAll
@Consumes(MyModelType.APPLICATION_JSON)
public MyModel create(final MyModel data) throws Exception {
return DataAccess.insert(data);
}
@GET
@Path("{id}")
@PermitAll
public MyModel get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(MyModel.class, id);
}
@PATCH
@Path("{id}")
@PermitAll
@Consumes(MyModelType.APPLICATION_JSON)
public MyModel patch(@PathParam("id") final Long id, final String jsonRequest) throws Exception {
DataAccess.updateWithJson(MyModel.class, id, jsonRequest);
return DataAccess.get(MyModel.class, id);
}
@DELETE
@Path("{id}")
@PermitAll
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(MyModel.class, id);
}
}

View File

@@ -0,0 +1,28 @@
package sample.archidata.basic.migration;
import java.util.List;
import org.kar.archidata.migration.MigrationSqlStep;
import sample.archidata.basic.model.MyModel;
public class Initialization extends MigrationSqlStep {
public static final List<Class<?>> CLASSES_BASE = List.of(MyModel.class);
@Override
public String getName() {
return "Initialization";
}
@Override
public void generateStep() throws Exception {
for(final Class<?> clazz : CLASSES_BASE) {
addClass(clazz);
}
addAction("""
ALTER TABLE `MyModel` AUTO_INCREMENT = 1000;
""",
// Only MySql support this request (fail in SQLite)
"mysql");
}
}

View File

@@ -0,0 +1,36 @@
package sample.archidata.basic.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
// needed for Swagger interface
@Entity
// Do not generate Null in Json serialization ==> prefer undefined
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MyModel extends GenericDataSoftDelete {
// Can not be NULL and max length is 300 (note default is 255)
@Column(nullable = false, length = 300)
public String name;
// Can be null and no max length
@Column(length = 0)
public String description;
@Override
public String toString() {
return "MyModel [name=" + this.name + ", description=" + this.description + "]";
}
}

View File

@@ -0,0 +1,5 @@
<FindBugsFilter>
<Match>
<Bug category="SECURITY"/>
</Match>
</FindBugsFilter>

View File

@@ -0,0 +1,2 @@
<FindBugsFilter>
</FindBugsFilter>

View File

@@ -4,10 +4,12 @@ import org.kar.archidata.db.DBConfig;
import org.kar.archidata.tools.ConfigBaseVariable;
public class GlobalConfiguration {
public static DBConfig dbConfig = null;
public static final DBConfig dbConfig;
static {
dbConfig = new DBConfig(ConfigBaseVariable.getDBType(), ConfigBaseVariable.getDBHost(), Integer.parseInt(ConfigBaseVariable.getDBPort()), ConfigBaseVariable.getDBLogin(),
ConfigBaseVariable.getDBPassword(), ConfigBaseVariable.getDBName(), ConfigBaseVariable.getDBKeepConnected());
dbConfig = new DBConfig(ConfigBaseVariable.getDBType(), ConfigBaseVariable.getDBHost(),
Integer.parseInt(ConfigBaseVariable.getDBPort()), ConfigBaseVariable.getDBLogin(),
ConfigBaseVariable.getDBPassword(), ConfigBaseVariable.getDBName(),
ConfigBaseVariable.getDBKeepConnected());
}
}

View File

@@ -5,9 +5,9 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryOption;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.DBInterfaceOption;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.model.User;
@@ -19,7 +19,8 @@ public class UserDB {
return DataAccess.get(User.class, userId, option);
}
public static User getUserOrCreate(final long userId, final String userLogin, QueryOption... option) throws Exception {
public static User getUserOrCreate(final long userId, final String userLogin, QueryOption... option)
throws Exception {
final User user = getUsers(userId);
if (user != null) {
return user;
@@ -28,7 +29,8 @@ public class UserDB {
return getUsers(userId);
}
private static void createUsersInfoFromOAuth(final long userId, final String login, QueryOption... option) throws IOException {
private static void createUsersInfoFromOAuth(final long userId, final String login, QueryOption... option)
throws IOException {
QueryOptions options = new QueryOptions(option);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final String query = "INSERT INTO `user` (`id`, `login`, `lastConnection`, `admin`, `blocked`, `removed`) VALUE (?,?,now(3),'0','0','0')";

View File

@@ -7,15 +7,19 @@ import java.util.List;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
@@ -27,24 +31,25 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws Exception {
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException {
if (options != null) {
final OverrideTableName data = options.get(OverrideTableName.class);
if (data != null) {
return data.getName();
final List<OverrideTableName> data = options.get(OverrideTableName.class);
if (data.size() == 1) {
return data.get(0).getName();
}
}
return AnnotationTools.getTableName(clazz);
}
public static String getTableName(final Class<?> element) throws Exception {
public static String getTableName(final Class<?> element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class);
if (annotation.length == 0) {
// when no annotation is detected, then the table name is the class name
return element.getSimpleName();
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Table on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Table on " + element.getClass().getCanonicalName());
}
final String tmp = ((Table) annotation[0]).name();
if (tmp == null) {
@@ -53,146 +58,179 @@ public class AnnotationTools {
return tmp;
}
public static boolean getSchemaReadOnly(final Field element) throws Exception {
public static boolean getSchemaReadOnly(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return false;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
}
return ((Schema) annotation[0]).readOnly();
}
public static String getSchemaExample(final Class<?> element) throws Exception {
public static String getSchemaExample(final Class<?> element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
}
return ((Schema) annotation[0]).example();
}
public static String getSchemaDescription(final Class<?> element) throws Exception {
public static boolean getNoWriteSpecificMode(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NoWriteSpecificMode.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static String getSchemaDescription(final Class<?> element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
}
return ((Schema) annotation[0]).description();
}
public static String getSchemaDescription(final Field element) throws Exception {
public static String getSchemaDescription(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
}
return ((Schema) annotation[0]).description();
}
public static String getComment(final Field element) throws Exception {
public static String getComment(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class);
if (annotation.length == 0) {
return getSchemaDescription(element);
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
}
return ((DataComment) annotation[0]).value();
}
public static String getDefault(final Field element) throws Exception {
public static String getDefault(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DefaultValue.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
}
return ((DefaultValue) annotation[0]).value();
}
public static ManyToOne getManyToOne(final Field element) throws Exception {
public static ManyToOne getManyToOne(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (ManyToOne) annotation[0];
}
public static DataJson getDataJson(final Field element) throws Exception {
public static ManyToMany getManyToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToMany.class);
if (annotation.length == 0) {
return null;
}
return (ManyToMany) annotation[0];
}
public static OneToMany getOneToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(OneToMany.class);
if (annotation.length == 0) {
return null;
}
return (OneToMany) annotation[0];
}
public static DataJson getDataJson(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (DataJson) annotation[0];
}
public static Long getConstraintsMax(final Field element) throws Exception {
public static Long getConstraintsMax(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Max) annotation[0]).value();
}
public static Long getConstraintsMin(final Field element) throws Exception {
public static Long getConstraintsMin(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Min) annotation[0]).value();
}
public static int getLimitSize(final Field element) throws Exception {
public static int getLimitSize(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return 255;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
final int length = ((Column) annotation[0]).length();
return length <= 0 ? 0 : length;
}
public static Size getConstraintsSize(final Field element) throws Exception {
public static Size getConstraintsSize(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return (Size) annotation[0];
}
public static String getConstraintsPattern(final Field element) throws Exception {
public static String getConstraintsPattern(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName());
}
return ((Pattern) annotation[0]).regexp();
}
@@ -220,13 +258,14 @@ public class AnnotationTools {
return false;
}
public static String getFieldName(final Field element) throws Exception {
public static String getFieldName(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return element.getName();
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
final String name = ((Column) annotation[0]).name();
if (name.isBlank()) {
@@ -235,29 +274,39 @@ public class AnnotationTools {
return name;
}
public static boolean getColumnNotNull(final Field element) throws Exception {
public static boolean getColumnNotNull(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return false;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
return !((Column) annotation[0]).nullable();
}
public static boolean getConstraintsNotNull(final Field element) throws Exception {
public static boolean getNullable(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Nullable.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static boolean getConstraintsNotNull(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NotNull.class);
if (annotation.length == 0) {
return false;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName());
}
return true;
}
public static Field getPrimaryKeyField(final Class<?> clazz) throws Exception {
public static Field getPrimaryKeyField(final Class<?> clazz) throws DataAccessException {
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@@ -270,7 +319,7 @@ public class AnnotationTools {
return null;
}
public static boolean isPrimaryKey(final Field element) throws Exception {
public static boolean isPrimaryKey(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class);
if (annotation.length == 0) {
return false;
@@ -278,49 +327,51 @@ public class AnnotationTools {
return true;
}
public static boolean isUnique(final Field element) throws Exception {
public static boolean isUnique(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return false;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
return ((Column) annotation[0]).unique();
}
public static GenerationType getStrategy(final Field element) throws Exception {
public static GenerationType getStrategy(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(GeneratedValue.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
return ((GeneratedValue) annotation[0]).strategy();
}
public static boolean isDeletedField(final Field element) throws Exception {
public static boolean isDeletedField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(DataDeleted.class).length != 0;
}
public static boolean isCreatedAtField(final Field element) throws Exception {
public static boolean isCreatedAtField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
}
public static boolean isUpdateAtField(final Field element) throws Exception {
public static boolean isUpdateAtField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
}
public static boolean isdefaultNotRead(final Field element) throws Exception {
public static boolean isdefaultNotRead(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(DataNotRead.class).length != 0;
}
public static boolean isIdField(final Field element) throws Exception {
public static boolean isIdField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(Id.class).length != 0;
}
public static String getDeletedFieldName(final Class<?> clazz) throws Exception {
public static String getDeletedFieldName(final Class<?> clazz) throws DataAccessException {
try {
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
@@ -337,7 +388,7 @@ public class AnnotationTools {
return null;
}
public static String getUpdatedFieldName(final Class<?> clazz) throws Exception {
public static String getUpdatedFieldName(final Class<?> clazz) throws DataAccessException {
try {
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
@@ -371,15 +422,16 @@ public class AnnotationTools {
return null;
}
public static List<String> getFieldsNames(final Class<?> clazz) throws Exception {
public static List<String> getFieldsNames(final Class<?> clazz) throws DataAccessException {
return getFieldsNamesFilter(clazz, false);
}
public static List<String> getAllFieldsNames(final Class<?> clazz) throws Exception {
public static List<String> getAllFieldsNames(final Class<?> clazz) throws DataAccessException {
return getFieldsNamesFilter(clazz, true);
}
private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full) throws Exception {
private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full)
throws DataAccessException {
final List<String> out = new ArrayList<>();
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
@@ -394,11 +446,12 @@ public class AnnotationTools {
return out;
}
public static boolean isGenericField(final Field elem) throws Exception {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
public static boolean isGenericField(final Field elem) throws DataAccessException {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem)
|| AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
}
public static Field getFieldOfId(final Class<?> clazz) throws Exception {
public static Field getFieldOfId(final Class<?> clazz) throws DataAccessException {
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@@ -411,7 +464,7 @@ public class AnnotationTools {
return null;
}
public static Field getFieldNamed(final Class<?> clazz, final String name) throws Exception {
public static Field getFieldNamed(final Class<?> clazz, final String name) throws DataAccessException {
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {

View File

@@ -5,8 +5,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** When we wend to have only One type for read and write mode (Wrapping API). */
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLWhere {
String clause();
public @interface NoWriteSpecificMode {
}

View File

@@ -5,6 +5,7 @@ import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -26,6 +27,7 @@ import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable;
@@ -85,7 +87,8 @@ public class DataResource {
}
public static String getFileDataOld(final long tmpFolderId) {
final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data";
final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator
+ "data";
try {
createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator);
} catch (final IOException e) {
@@ -100,7 +103,8 @@ public class DataResource {
final String part2 = stringUUID.substring(2, 4);
final String part3 = stringUUID.substring(4);
final String finalPath = part1 + File.separator + part2;
String filePath = ConfigBaseVariable.getMediaDataFolder() + "_uuid" + File.separator + finalPath + File.separator;
String filePath = ConfigBaseVariable.getMediaDataFolder() + "_uuid" + File.separator + finalPath
+ File.separator;
try {
createFolder(filePath);
} catch (final IOException e) {
@@ -136,19 +140,20 @@ public class DataResource {
return null;
}
public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512) throws IOException {
public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512)
throws IOException {
// determine mime type:
Data injectedData = new Data();
String mimeType = "";
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
mimeType = switch (extension.toLowerCase()) {
case "jpg", "jpeg" -> "image/jpeg";
case "png" -> "image/png";
case "webp" -> "image/webp";
case "mka" -> "audio/x-matroska";
case "mkv" -> "video/x-matroska";
case "webm" -> "video/webm";
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
case "jpg", "jpeg" -> "image/jpeg";
case "png" -> "image/png";
case "webp" -> "image/webp";
case "mka" -> "audio/x-matroska";
case "mkv" -> "video/x-matroska";
case "webm" -> "video/webm";
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
};
injectedData.mimeType = mimeType;
injectedData.sha512 = sha512;
@@ -188,7 +193,8 @@ public class DataResource {
LOGGER.info("Move done");
}
public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) {
public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData)
throws FailException {
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
}
@@ -205,35 +211,35 @@ public class DataResource {
}
// save uploaded file to a defined location on the server
static String saveFile(final InputStream uploadedInputStream, final String serverLocation) {
static String saveFile(final InputStream uploadedInputStream, final String serverLocation) throws FailException {
String out = "";
try {
OutputStream outpuStream = new FileOutputStream(new File(serverLocation));
int read = 0;
final byte[] bytes = new byte[CHUNK_SIZE_IN];
final MessageDigest md = MessageDigest.getInstance("SHA-512");
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
// logger.info("write {}", read);
md.update(bytes, 0, read);
outpuStream.write(bytes, 0, read);
}
LOGGER.info("Flush input stream ... {}", serverLocation);
System.out.flush();
outpuStream.flush();
outpuStream.close();
// create the end of sha512
final byte[] sha512Digest = md.digest();
// convert in hexadecimal
out = bytesToHex(sha512Digest);
uploadedInputStream.close();
MessageDigest md = null;
try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
md = MessageDigest.getInstance("SHA-512");
} catch (final IOException ex) {
LOGGER.info("Can not write in temporary file ... ");
ex.printStackTrace();
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
} catch (final NoSuchAlgorithmException ex) {
LOGGER.info("Can not find sha512 algorithms");
ex.printStackTrace();
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not find sha512 algorithms", ex);
}
if (md != null) {
try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
int read = 0;
final byte[] bytes = new byte[CHUNK_SIZE_IN];
while ((read = uploadedInputStream.read(bytes)) != -1) {
// logger.info("write {}", read);
md.update(bytes, 0, read);
outpuStream.write(bytes, 0, read);
}
LOGGER.info("Flush input stream ... {}", serverLocation);
outpuStream.flush();
// create the end of sha512
final byte[] sha512Digest = md.digest();
// convert in hexadecimal
out = bytesToHex(sha512Digest);
uploadedInputStream.close();
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
}
}
return out;
}
@@ -261,7 +267,10 @@ public class DataResource {
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@RolesAllowed("ADMIN")
@Operation(description = "Insert a new data in the data environment", tags = "SYSTEM")
public void uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) {
public void uploadFile(
@Context final SecurityContext sc,
@FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws FailException {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.info("===================================================");
LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken));
@@ -271,8 +280,9 @@ public class DataResource {
final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
try {
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
} catch (final IOException e) {
e.printStackTrace();
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
"Impossible to create the folder in the server", ex);
}
saveFile(fileInputStream, filePath);
}
@@ -283,17 +293,25 @@ public class DataResource {
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get back some data from the data environment", tags = "SYSTEM")
public Response retrieveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws Exception {
public Response retrieveDataId(
@Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws FailException {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
LOGGER.info("== DATA retrieveDataId ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken));
// logger.info("===================================================");
final Data value = getSmall(uuid);
if (value == null) {
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
return Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
try {
return buildStream(getFileData(uuid), range,
value.mimeType == null ? "application/octet-stream" : value.mimeType);
} catch (final Exception ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
}
return buildStream(getFileData(uuid), range, value.mimeType);
}
@GET
@@ -303,8 +321,11 @@ public class DataResource {
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get a thumbnail of from the data environment (if resize is possible)", tags = "SYSTEM")
// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retrieveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws Exception {
public Response retrieveDataThumbnailId(
@Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws FailException {
// GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
// logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user));
@@ -316,15 +337,22 @@ public class DataResource {
final String filePathName = getFileData(uuid);
final File inputFile = new File(filePathName);
if (!inputFile.exists()) {
return Response.status(404).entity("{\"error\":\"media Does not exist: " + uuid + "\"}").type("application/json").build();
return Response.status(404).entity("{\"error\":\"media Does not exist: " + uuid + "\"}")
.type("application/json").build();
}
if (value.mimeType.contentEquals("image/jpeg") || value.mimeType.contentEquals("image/png")
// || value.mimeType.contentEquals("image/webp")
) {
// reads input image
final BufferedImage inputImage = ImageIO.read(inputFile);
BufferedImage inputImage;
try {
inputImage = ImageIO.read(inputFile);
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to READ the image", ex);
}
final int scaledWidth = 250;
final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth() * scaledWidth);
final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth()
* scaledWidth);
// creates output image
final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType());
@@ -339,11 +367,13 @@ public class DataResource {
ImageIO.write(outputImage, "JPG", baos);
} catch (final IOException e) {
e.printStackTrace();
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain").build();
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain")
.build();
}
final byte[] imageData = baos.toByteArray();
// Response.ok(new ByteArrayInputStream(imageData)).build();
final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH, imageData.length);
final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH,
imageData.length);
out.type("image/jpeg");
// TODO: move this in a decorator !!!
final CacheControl cc = new CacheControl();
@@ -352,7 +382,11 @@ public class DataResource {
out.cacheControl(cc);
return out.build();
}
return buildStream(filePathName, range, value.mimeType);
try {
return buildStream(filePathName, range, value.mimeType);
} catch (final Exception ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
}
}
@GET
@@ -361,25 +395,32 @@ public class DataResource {
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get back some data from the data environment (with a beautiful name (permit download with basic name)", tags = "SYSTEM")
public Response retrieveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid, @PathParam("name") final String name) throws Exception {
public Response retrieveDataFull(
@Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid,
@PathParam("name") final String name) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
LOGGER.info("== DATA retrieveDataFull ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken));
// logger.info("===================================================");
final Data value = getSmall(uuid);
if (value == null) {
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
return Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
return buildStream(getFileData(uuid), range, value.mimeType);
return buildStream(getFileData(uuid), range,
value.mimeType == null ? "application/octet-stream" : value.mimeType);
}
/** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541
*
* @param range range header
* @return Streaming output
* @throws FileNotFoundException
* @throws Exception IOException if an error occurs in streaming. */
private Response buildStream(final String filename, final String range, final String mimeType) throws Exception {
private Response buildStream(final String filename, final String range, final String mimeType)
throws FailException {
final File file = new File(filename);
// logger.info("request range : {}", range);
// range not requested : Firefox does not send range headers
@@ -426,17 +467,24 @@ public class DataResource {
}
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
// logger.info("responseRange: {}", responseRange);
final RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.seek(from);
try (final RandomAccessFile raf = new RandomAccessFile(file, "r")) {
raf.seek(from);
final long len = to - from + 1;
final MediaStreamer streamer = new MediaStreamer(len, raf);
final Response.ResponseBuilder out = Response.ok(streamer).status(Response.Status.PARTIAL_CONTENT).header("Accept-Ranges", "bytes").header("Content-Range", responseRange)
.header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth()).header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified()));
if (mimeType != null) {
out.type(mimeType);
final long len = to - from + 1;
final MediaStreamer streamer = new MediaStreamer(len, raf);
final Response.ResponseBuilder out = Response.ok(streamer).status(Response.Status.PARTIAL_CONTENT)
.header("Accept-Ranges", "bytes").header("Content-Range", responseRange)
.header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth())
.header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified()));
if (mimeType != null) {
out.type(mimeType);
}
return out.build();
} catch (final FileNotFoundException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to find the required file.", ex);
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to access to the required file.", ex);
}
return out.build();
}
public static void undelete(final Long id) throws Exception {

View File

@@ -31,7 +31,8 @@ public class MediaStreamer implements StreamingOutput {
public void write(final OutputStream outputStream) {
try {
while (this.length != 0) {
final int read = this.raf.read(this.buf, 0, this.buf.length > this.length ? (int) this.length : this.buf.length);
final int read = this.raf.read(this.buf, 0,
this.buf.length > this.length ? (int) this.length : this.buf.length);
try {
outputStream.write(this.buf, 0, read);
} catch (final IOException ex) {

View File

@@ -27,7 +27,8 @@ public class openApiResource extends BaseOpenApiResource {
@Produces({ MediaType.APPLICATION_JSON })
@PermitAll
@Operation(hidden = true, description = "Get the OPEN-API description", tags = "SYSTEM")
public Response getDescription(@Context final HttpHeaders headers, @Context final UriInfo uriInfo) throws Exception {
public Response getDescription(@Context final HttpHeaders headers, @Context final UriInfo uriInfo)
throws Exception {
return getOpenApi(headers, this.config, this.app, uriInfo, "json");
}
}

View File

@@ -16,11 +16,13 @@ public class ExceptionCatcher implements ExceptionMapper<Exception> {
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON).build();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON)
.build();
}
private RestErrorResponse build(final Exception exception) {
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch Unknown Exception", exception.getMessage());
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR,
"Catch Unknown Exception: " + exception.getClass().getCanonicalName(), exception.getMessage());
}
}

View File

@@ -1,25 +0,0 @@
package org.kar.archidata.catcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.ClientErrorException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class FailException404API implements ExceptionMapper<ClientErrorException> {
private static final Logger LOGGER = LoggerFactory.getLogger(FailException404API.class);
@Override
public Response toResponse(final ClientErrorException exception) {
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
return Response.status(exception.getResponse().getStatusInfo().toEnum()).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final ClientErrorException exception) {
return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception", exception.getMessage());
}
}

View File

@@ -13,11 +13,12 @@ public class FailExceptionCatcher implements ExceptionMapper<FailException> {
@Override
public Response toResponse(final FailException exception) {
LOGGER.warn("Catch FailException:");
LOGGER.warn("Catch FailException: {}", exception.getLocalizedMessage());
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
// Not display backtrace ==> this may be a normal case ...
// exception.printStackTrace();
if (exception.exception != null) {
exception.exception.printStackTrace();
}
return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build();
}

View File

@@ -0,0 +1,24 @@
package org.kar.archidata.catcher;
import org.glassfish.jersey.server.ResourceConfig;
public class GenericCatcher {
/**
* Add All the the generic catcher to standardize returns.
* @param rc Resource exception model.
*/
public static void addAll(final ResourceConfig rc) {
// Generic Json parsing error
rc.register(JacksonExceptionCatcher.class);
// Catch jakarta generic errors
rc.register(WebApplicationExceptionCatcher.class);
// Archidata exceptions
rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
// generic Exception catcher
rc.register(ExceptionCatcher.class);
}
}

View File

@@ -15,13 +15,15 @@ public class InputExceptionCatcher implements ExceptionMapper<InputException> {
public Response toResponse(final InputException exception) {
LOGGER.warn("Catch InputException:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={} ==> '{}'=>'{}'", ret.uuid, exception.missingVariable, exception.getLocalizedMessage());
LOGGER.error("Error UUID={} ==> '{}'=>'{}'", ret.uuid, exception.missingVariable,
exception.getLocalizedMessage());
// exception.printStackTrace();
return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final InputException exception) {
return new RestErrorResponse(exception.status, "Error on input='" + exception.missingVariable + "'", exception.getMessage());
return new RestErrorResponse(exception.status, "Error on input='" + exception.missingVariable + "'",
exception.getMessage());
}
}

View File

@@ -3,26 +3,28 @@ package org.kar.archidata.catcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JacksonException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class JacksonCatcher implements ExceptionMapper<JsonProcessingException> {
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonCatcher.class);
public class JacksonExceptionCatcher implements ExceptionMapper<JacksonException> {
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonExceptionCatcher.class);
@Override
public Response toResponse(final JsonProcessingException exception) {
public Response toResponse(final JacksonException exception) {
LOGGER.warn("Catch exception Input data parsing:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON).build();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON)
.build();
}
private RestErrorResponse build(final Exception exception) {
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch JSON Exception", exception.getMessage());
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch JSON Exception",
exception.getMessage());
}
}

View File

@@ -3,19 +3,33 @@ package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.UUID;
import org.kar.archidata.annotation.NoWriteSpecificMode;
import org.kar.archidata.tools.UuidUtils;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response;
@NoWriteSpecificMode
public class RestErrorResponse {
public UUID uuid = UuidUtils.nextUUID();
@NotNull
@Column(length = 0)
public String name; // Mandatory for TS generic error
@NotNull
@Column(length = 0)
public String message; // Mandatory for TS generic error
@NotNull
@Column(length = 0)
public String time;
@NotNull
final public int status;
@NotNull
@Column(length = 0)
final public String statusMessage;
public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) {
public RestErrorResponse(final Response.Status status, final String time, final String error,
final String message) {
this.time = time;
this.name = error;
this.message = message;

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.catcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class WebApplicationExceptionCatcher implements ExceptionMapper<WebApplicationException> {
private static final Logger LOGGER = LoggerFactory.getLogger(WebApplicationExceptionCatcher.class);
@Override
public Response toResponse(final WebApplicationException exception) {
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
return Response.status(exception.getResponse().getStatusInfo().toEnum()).entity(ret)
.type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final WebApplicationException exception) {
return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception",
exception.getMessage());
}
}

View File

@@ -25,12 +25,16 @@ import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOn.AddOnSQLTableExternalForeinKeyAsList;
import org.kar.archidata.dataAccess.addOn.AddOnOneToMany;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceOption;
import org.kar.archidata.dataAccess.options.DBInterfaceRoot;
import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.GroupBy;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.TransmitKey;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.exception.DataAccessException;
@@ -43,6 +47,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.InternalServerErrorException;
@@ -60,7 +65,7 @@ public class DataAccess {
static {
addOn.add(new AddOnManyToMany());
addOn.add(new AddOnManyToOne());
addOn.add(new AddOnSQLTableExternalForeinKeyAsList());
addOn.add(new AddOnOneToMany());
addOn.add(new AddOnDataJson());
}
@@ -74,7 +79,8 @@ public class DataAccess {
}
public static boolean isDBExist(final String name, final QueryOption... option) throws InternalServerErrorException {
public static boolean isDBExist(final String name, final QueryOption... option)
throws InternalServerErrorException {
final QueryOptions options = new QueryOptions(option);
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
// no base manage in sqLite ...
@@ -134,7 +140,8 @@ public class DataAccess {
}
}
public static boolean isTableExist(final String name, final QueryOption... option) throws InternalServerErrorException {
public static boolean isTableExist(final String name, final QueryOption... option)
throws InternalServerErrorException {
final QueryOptions options = new QueryOptions(option);
try {
String request = "";
@@ -180,7 +187,8 @@ public class DataAccess {
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the SQL request. */
public static List<Long> getListOfIds(final ResultSet rs, final int iii, final String separator) throws SQLException {
public static List<Long> getListOfIds(final ResultSet rs, final int iii, final String separator)
throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
@@ -199,7 +207,8 @@ public class DataAccess {
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the SQL request. */
public static List<UUID> getListOfUUIDs(final ResultSet rs, final int iii, final String separator) throws SQLException {
public static List<UUID> getListOfUUIDs(final ResultSet rs, final int iii, final String separator)
throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
@@ -227,7 +236,8 @@ public class DataAccess {
return groups;
}
public static List<UUID> getListOfRawUUIDs(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
public static List<UUID> getListOfRawUUIDs(final ResultSet rs, final int iii)
throws SQLException, DataAccessException {
final byte[] trackString = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
@@ -241,7 +251,20 @@ public class DataAccess {
return out;
}
protected static <T> void setValuedb(final Class<?> type, final T data, final CountInOut iii, final Field field, final PreparedStatement ps) throws Exception {
public static UUID getListOfRawUUID(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
final byte[] elem = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
}
return UuidUtils.asUuid(elem);
}
protected static <T> void setValuedb(
final Class<?> type,
final T data,
final CountInOut iii,
final Field field,
final PreparedStatement ps) throws Exception {
if (type == UUID.class) {
final Object tmp = field.get(data);
if (tmp == null) {
@@ -354,7 +377,13 @@ public class DataAccess {
iii.inc();
}
protected static <T> void setValueFromDb(final Class<?> type, final Object data, final CountInOut count, final Field field, final ResultSet rs, final CountInOut countNotNull) throws Exception {
protected static <T> void setValueFromDb(
final Class<?> type,
final Object data,
final CountInOut count,
final Field field,
final ResultSet rs,
final CountInOut countNotNull) throws Exception {
if (type == UUID.class) {
final byte[] tmp = rs.getBytes(count.value);
// final UUID tmp = rs.getObject(count.value, UUID.class);
@@ -769,14 +798,15 @@ public class DataAccess {
return out;
}
@SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public static <T> T insert(final T data, final QueryOption... option) throws Exception {
final Class<?> clazz = data.getClass();
final QueryOptions options = new QueryOptions(option);
// External checker of data:
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check("", data, AnnotationTools.getFieldsNames(clazz));
final List<CheckFunction> checks = options.get(CheckFunction.class);
for (final CheckFunction check : checks) {
check.getChecker().check("", data, AnnotationTools.getFieldsNames(clazz), options);
}
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
@@ -871,13 +901,14 @@ public class DataAccess {
query.append("?");
}
query.append(")");
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuery(query, tableName);
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.generateQuery(query, tableName);
}
LOGGER.warn("generate the query: '{}'", query.toString());
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
UUID uuid = null;
@@ -940,13 +971,12 @@ public class DataAccess {
// uniqueSQLUUID = generatedKeys.getObject(1, UUID.class);
/* final Object obj = generatedKeys.getObject(1); final BigInteger bigint = (BigInteger) generatedKeys.getObject(1); uniqueSQLUUID = UuidUtils.asUuid(bigint); final UUID
* generatedUUID = (UUID) generatedKeys.getObject(1); System.out.println("UUID généré: " + generatedUUID); */
final Object obj = generatedKeys.getObject(1);
//final Object obj = generatedKeys.getObject(1);
final byte[] tmpid = generatedKeys.getBytes(1);
uniqueSQLUUID = UuidUtils.asUuid(tmpid);
} else {
uniqueSQLID = generatedKeys.getLong(1);
}
} else {
throw new SQLException("Creating node failed, no ID obtained (1).");
}
@@ -956,6 +986,7 @@ public class DataAccess {
throw new SQLException("Creating node failed, no ID obtained (2).");
}
}
ps.close();
if (primaryKeyField != null) {
if (primaryKeyField.getType() == Long.class) {
primaryKeyField.set(data, uniqueSQLID);
@@ -998,11 +1029,13 @@ public class DataAccess {
return insert(data);
}
public static <ID_TYPE> QueryCondition getTableIdCondition(final Class<?> clazz, final ID_TYPE idKey) throws Exception {
public static <ID_TYPE> QueryCondition getTableIdCondition(final Class<?> clazz, final ID_TYPE idKey)
throws DataAccessException {
// Find the ID field type ....
final Field idField = AnnotationTools.getIdField(clazz);
if (idField == null) {
throw new DataAccessException("The class have no annotation @Id ==> can not determine the default type searching");
throw new DataAccessException(
"The class have no annotation @Id ==> can not determine the default type searching");
}
// check the compatibility of the id and the declared ID
final Class<?> typeClass = idField.getType();
@@ -1011,7 +1044,8 @@ public class DataAccess {
}
if (idKey.getClass() != typeClass) {
if (idKey.getClass() == Condition.class) {
throw new DataAccessException("Try to identify the ID type on a condition 'close' internal API error use xxxWhere(...) instead.");
throw new DataAccessException(
"Try to identify the ID type on a condition 'close' internal API error use xxxWhere(...) instead.");
}
throw new DataAccessException("Request update with the wrong type ...");
}
@@ -1027,21 +1061,21 @@ public class DataAccess {
* @param jsonData Json data (partial) values to update
* @return the number of object updated
* @throws Exception */
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData, final QueryOption... option) throws Exception {
public static <T, ID_TYPE> int updateWithJson(
final Class<T> clazz,
final ID_TYPE id,
final String jsonData,
final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition != null) {
throw new DataAccessException("request a updateWithJson with a condition");
}
options.add(new Condition(getTableIdCondition(clazz, id)));
options.add(new TransmitKey(id));
return updateWhereWithJson(clazz, jsonData, options.getAllArray());
}
public static <T> int updateWhereWithJson(final Class<T> clazz, final String jsonData, final QueryOption... option) throws Exception {
public static <T> int updateWhereWithJson(final Class<T> clazz, final String jsonData, final QueryOption... option)
throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
if (options.get(Condition.class).size() == 0) {
throw new DataAccessException("request a updateWhereWithJson without any condition");
}
final ObjectMapper mapper = new ObjectMapper();
@@ -1066,7 +1100,11 @@ public class DataAccess {
* @param filterValue
* @return the affected rows.
* @throws Exception */
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id, final List<String> updateColomn, final QueryOption... option) throws Exception {
public static <T, ID_TYPE> int update(
final T data,
final ID_TYPE id,
final List<String> updateColomn,
final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(data.getClass(), id)));
options.add(new FilterValue(updateColomn));
@@ -1079,21 +1117,23 @@ public class DataAccess {
return updateWhere(data, options);
}
public static <T> int updateWhere(final T data, final QueryOptions options) throws Exception {
@SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public static <T> int updateWhere(final T data, QueryOptions options) throws Exception {
final Class<?> clazz = data.getClass();
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
if (options == null) {
options = new QueryOptions();
}
final FilterValue filter = options.get(FilterValue.class);
if (filter == null) {
throw new DataAccessException("request a gets without any filter values");
final Condition condition = conditionFusionOrEmpty(options, true);
final List<FilterValue> filters = options != null ? options.get(FilterValue.class) : new ArrayList<>();
if (filters.size() != 1) {
throw new DataAccessException("request a gets without/or with more 1 filter of values");
}
final FilterValue filter = filters.get(0);
// External checker of data:
if (options != null) {
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check("", data, filter.getValues());
final List<CheckFunction> checks = options.get(CheckFunction.class);
for (final CheckFunction check : checks) {
check.getChecker().check("", data, filter.getValues(), options);
}
}
final List<LazyGetter> asyncActions = new ArrayList<>();
@@ -1122,11 +1162,12 @@ public class DataAccess {
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
if (addOn.isInsertAsync(field)) {
final TransmitKey transmitKey = options.get(TransmitKey.class);
if (transmitKey == null) {
throw new DataAccessException("Fail to transmit Key to update the async update...");
final List<TransmitKey> transmitKey = options.get(TransmitKey.class);
if (transmitKey.size() != 1) {
throw new DataAccessException(
"Fail to transmit Key to update the async update... (must have only 1)");
}
addOn.asyncUpdate(tableName, transmitKey.getKey(), field, field.get(data), asyncActions);
addOn.asyncUpdate(tableName, transmitKey.get(0).getKey(), field, field.get(data), asyncActions);
}
continue;
}
@@ -1146,9 +1187,9 @@ public class DataAccess {
query.append("` = ? ");
}
query.append(" ");
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuery(query, tableName);
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.generateQuery(query, tableName);
}
query.append(" ");
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
@@ -1156,40 +1197,43 @@ public class DataAccess {
// If the first field is not set, then nothing to update n the main base:
if (!firstField) {
LOGGER.debug("generate the query: '{}'", query.toString());
LOGGER.debug("generate update query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (!filter.getValues().contains(name)) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
continue;
}
if (addOn == null) {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
final Object tmp = field.get(data);
if (tmp == null && field.getDeclaredAnnotationsByType(DefaultValue.class).length != 0) {
continue;
}
try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS)) {
final CountInOut iii = new CountInOut(1);
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (!filter.getValues().contains(name)) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
continue;
}
if (addOn == null) {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
final Object tmp = field.get(data);
if (tmp == null && field.getDeclaredAnnotationsByType(DefaultValue.class).length != 0) {
continue;
}
}
setValuedb(type, data, iii, field, ps);
} else {
addOn.insertData(ps, field, data, iii);
}
setValuedb(type, data, iii, field, ps);
} else {
addOn.insertData(ps, field, data, iii);
}
condition.injectQuery(ps, iii);
final int out = ps.executeUpdate();
return out;
}
condition.injectQuery(ps, iii);
return ps.executeUpdate();
}
} catch (final SQLException ex) {
ex.printStackTrace();
@@ -1200,7 +1244,8 @@ public class DataAccess {
return 0;
}
static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii) throws Exception {
public static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii)
throws Exception {
if (value instanceof final UUID tmp) {
final byte[] dataByte = UuidUtils.asBytes(tmp);
ps.setBytes(iii.value, dataByte);
@@ -1248,22 +1293,25 @@ public class DataAccess {
}
}
public static int executeSimpleQuery(final String query, final QueryOption... option) throws SQLException, IOException {
public static int executeSimpleQuery(final String query, final QueryOption... option)
throws SQLException, IOException {
final QueryOptions options = new QueryOptions(option);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final Statement stmt = entry.connection.createStatement();
return stmt.executeUpdate(query);
try (final Statement stmt = entry.connection.createStatement()) {
return stmt.executeUpdate(query);
}
}
public static boolean executeQuery(final String query, final QueryOption... option) throws SQLException, IOException {
public static boolean executeQuery(final String query, final QueryOption... option)
throws SQLException, IOException {
final QueryOptions options = new QueryOptions(option);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final Statement stmt = entry.connection.createStatement();
return stmt.execute(query);
try (final Statement stmt = entry.connection.createStatement()) {
return stmt.execute(query);
}
}
public static <T> T getWhere(final Class<T> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
public static <T> T getWhere(final Class<T> clazz, final QueryOptions options) throws Exception {
options.add(new Limit(1));
final List<T> values = getsWhere(clazz, options);
if (values.size() == 0) {
@@ -1272,6 +1320,11 @@ public class DataAccess {
return values.get(0);
}
public static <T> T getWhere(final Class<T> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
return getWhere(clazz, options);
}
public static void generateSelectField(//
final StringBuilder querySelect, //
final StringBuilder query, //
@@ -1320,18 +1373,40 @@ public class DataAccess {
return getsWhere(clazz, options);
}
@SuppressWarnings("unchecked")
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options) throws Exception {
Condition condition = options.get(Condition.class);
if (condition == null) {
condition = new Condition();
public static Condition conditionFusionOrEmpty(final QueryOptions options, final boolean throwIfEmpty)
throws DataAccessException {
if (options == null) {
return new Condition();
}
final List<Condition> conditions = options.get(Condition.class);
if (conditions.size() == 0) {
if (throwIfEmpty) {
throw new DataAccessException("request a gets without any condition");
} else {
return new Condition();
}
}
Condition condition = null;
if (conditions.size() == 1) {
condition = conditions.get(0);
} else {
final QueryAnd andCondition = new QueryAnd();
for (final Condition cond : conditions) {
andCondition.add(cond.condition);
}
condition = new Condition(andCondition);
}
return condition;
}
@SuppressWarnings("unchecked")
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options)
throws DataAccessException, IOException {
final Condition condition = conditionFusionOrEmpty(options, false);
final List<LazyGetter> lazyCall = new ArrayList<>();
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final List<T> outs = new ArrayList<>();
// real add in the BDD:
try {
try (final DBEntry entry = DBInterfaceOption.getAutoEntry(options)) {
final CountInOut count = new CountInOut();
final StringBuilder querySelect = new StringBuilder();
StringBuilder query = new StringBuilder();
@@ -1345,51 +1420,59 @@ public class DataAccess {
querySelect.append(query.toString());
query = querySelect;
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
final GroupBy groups = options.get(GroupBy.class);
if (groups != null) {
groups.generateQuery(query, null);
final List<GroupBy> groups = options.get(GroupBy.class);
for (final GroupBy group : groups) {
group.generateQuery(query, tableName);
}
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuery(query, tableName);
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.generateQuery(query, tableName);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuery(query, tableName);
final List<Limit> limits = options.get(Limit.class);
if (limits.size() == 1) {
limits.get(0).generateQuery(query, tableName);
} else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'...");
}
LOGGER.warn("generate the query: '{}'", query.toString());
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii);
if (limit != null) {
limit.injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
while (rs.next()) {
count.value = 1;
final CountInOut countNotNull = new CountInOut(0);
final Object data = createObjectFromSQLRequest(rs, clazz, count, countNotNull, options, lazyCall);
final T out = (T) data;
outs.add(out);
}
LOGGER.info("Async calls: {}", lazyCall.size());
for (final LazyGetter elem : lazyCall) {
elem.doRequest();
try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS)) {
final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii);
if (limits.size() == 1) {
limits.get(0).injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
while (rs.next()) {
count.value = 1;
final CountInOut countNotNull = new CountInOut(0);
final Object data = createObjectFromSQLRequest(rs, clazz, count, countNotNull, options, lazyCall);
final T out = (T) data;
outs.add(out);
}
LOGGER.info("Async calls: {}", lazyCall.size());
for (final LazyGetter elem : lazyCall) {
elem.doRequest();
}
}
} catch (final SQLException ex) {
ex.printStackTrace();
throw ex;
throw new DataAccessException("Catch a SQL Exception: " + ex.getMessage());
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
entry.close();
throw new DataAccessException("Catch an Exception: " + ex.getMessage());
}
return outs;
}
public static Object createObjectFromSQLRequest(final ResultSet rs, final Class<?> clazz, final CountInOut count, final CountInOut countNotNull, final QueryOptions options,
public static Object createObjectFromSQLRequest(
final ResultSet rs,
final Class<?> clazz,
final CountInOut count,
final CountInOut countNotNull,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
final boolean readAllfields = QueryOptions.readAllColomn(options);
// TODO: manage class that is defined inside a class ==> Not manage for now...
@@ -1400,7 +1483,8 @@ public class DataAccess {
}
}
if (data == null) {
throw new DataAccessException("Can not find the default constructor for the class: " + clazz.getCanonicalName());
throw new DataAccessException(
"Can not find the default constructor for the class: " + clazz.getCanonicalName());
}
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
@@ -1424,16 +1508,20 @@ public class DataAccess {
return data;
}
public static <ID_TYPE> long count(final Class<?> clazz, final ID_TYPE id) throws Exception {
return DataAccess.countWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
public static <ID_TYPE> long count(final Class<?> clazz, final ID_TYPE id, final QueryOption... option)
throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return DataAccess.countWhere(clazz, options);
}
public static long countWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
Condition condition = options.get(Condition.class);
if (condition == null) {
condition = new Condition();
}
return countWhere(clazz, options);
}
public static long countWhere(final Class<?> clazz, final QueryOptions options) throws Exception {
final Condition condition = conditionFusionOrEmpty(options, false);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
DBEntry entry = DBInterfaceOption.getAutoEntry(options);
long count = 0;
@@ -1445,17 +1533,20 @@ public class DataAccess {
query.append(tableName);
query.append("` ");
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuery(query, tableName);
final List<Limit> limits = options.get(Limit.class);
if (limits.size() == 1) {
limits.get(0).generateQuery(query, tableName);
} else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'...");
}
LOGGER.warn("generate the query: '{}'", query.toString());
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii);
if (limit != null) {
limit.injectQuery(ps, iii);
if (limits.size() == 1) {
limits.get(0).injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
@@ -1474,7 +1565,8 @@ public class DataAccess {
return count;
}
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... option)
throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return DataAccess.getWhere(clazz, options.getAllArray());
@@ -1488,17 +1580,14 @@ public class DataAccess {
return getsWhere(clazz, option);
}
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id) throws Exception {
return delete(clazz, id, null);
}
/** Delete items with the specific Id (cf @Id) and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param <ID_TYPE> Type of the reference @Id
* @param clazz Data model that might remove element
* @param id Unique Id of the model
* @param options (Optional) Options of the request
* @return Number of element that is removed. */
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOption... options) throws Exception {
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOption... options)
throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
return deleteSoft(clazz, id, options);
@@ -1522,7 +1611,8 @@ public class DataAccess {
}
}
public static <ID_TYPE> int deleteHard(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
public static <ID_TYPE> int deleteHard(final Class<?> clazz, final ID_TYPE id, final QueryOption... option)
throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return deleteHardWhere(clazz, options.getAllArray());
@@ -1530,10 +1620,7 @@ public class DataAccess {
public static int deleteHardWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final Condition condition = conditionFusionOrEmpty(options, true);
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
// find the deleted field
@@ -1554,7 +1641,8 @@ public class DataAccess {
}
}
private static <ID_TYPE> int deleteSoft(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
private static <ID_TYPE> int deleteSoft(final Class<?> clazz, final ID_TYPE id, final QueryOption... option)
throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return deleteSoftWhere(clazz, options.getAllArray());
@@ -1562,10 +1650,7 @@ public class DataAccess {
public static int deleteSoftWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final Condition condition = conditionFusionOrEmpty(options, true);
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
/* String updateFieldName = null; if ("sqlite".equalsIgnoreCase(ConfigBaseVariable.getDBType())) { updateFieldName = AnnotationTools.getUpdatedFieldName(clazz); } */
@@ -1591,28 +1676,31 @@ public class DataAccess {
}
}
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id) throws Exception {
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id) throws DataAccessException {
return unsetDeleteWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
}
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOption... option)
throws DataAccessException {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return unsetDeleteWhere(clazz, options.getAllArray());
}
public static int unsetDeleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
public static int unsetDeleteWhere(final Class<?> clazz, final QueryOption... option) throws DataAccessException {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final Condition condition = conditionFusionOrEmpty(options, true);
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (deletedFieldName == null) {
throw new DataAccessException("The class " + clazz.getCanonicalName() + " has no deleted field");
}
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
DBEntry entry;
try {
entry = DBInterfaceOption.getAutoEntry(options);
} catch (final IOException ex) {
throw new DataAccessException("Fail to connect the DB: " + ex.getMessage());
}
final StringBuilder query = new StringBuilder();
query.append("UPDATE `");
query.append(tableName);
@@ -1622,13 +1710,14 @@ public class DataAccess {
// need to disable the deleted false because the model must be unselected to be updated.
options.add(QueryOptions.ACCESS_DELETED_ITEMS);
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
try {
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString())) {
final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii);
return ps.executeUpdate();
} finally {
entry.close();
} catch (final SQLException ex) {
throw new DataAccessException("Catch SQL error:" + ex.getMessage());
} catch (final Exception ex) {
throw new DataAccessException("Fail to excute the SQL query:" + ex.getMessage());
}
}
@@ -1705,20 +1794,25 @@ public class DataAccess {
* @param option Optional parameters
* @return The list of element requested
* @throws Exception */
public static <TYPE> List<TYPE> query(final Class<TYPE> clazz, final String query, final List<Object> parameters, final QueryOption... option) throws Exception {
public static <TYPE> List<TYPE> query(
final Class<TYPE> clazz,
final String query,
final List<Object> parameters,
final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
return query(clazz, query, parameters, options);
}
public static <TYPE> List<TYPE> query(final Class<TYPE> clazz, final String queryBase, final List<Object> parameters, final QueryOptions options) throws Exception {
public static <TYPE> List<TYPE> query(
final Class<TYPE> clazz,
final String queryBase,
final List<Object> parameters,
final QueryOptions options) throws Exception {
final List<LazyGetter> lazyCall = new ArrayList<>();
// TODO ... final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
Condition condition = options.get(Condition.class);
if (condition == null) {
condition = new Condition();
}
final Condition condition = conditionFusionOrEmpty(options, false);
final StringBuilder query = new StringBuilder(queryBase);
final List<TYPE> outs = new ArrayList<>();
// real add in the BDD:
@@ -1726,21 +1820,24 @@ public class DataAccess {
final CountInOut count = new CountInOut();
condition.whereAppendQuery(query, null, options, null);
final GroupBy groups = options.get(GroupBy.class);
if (groups != null) {
groups.generateQuery(query, null);
final List<GroupBy> groups = options.get(GroupBy.class);
for (final GroupBy group : groups) {
group.generateQuery(query, null);
}
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuery(query, null);
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.generateQuery(query, null);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuery(query, null);
final List<Limit> limits = options.get(Limit.class);
if (limits.size() == 1) {
limits.get(0).generateQuery(query, null);
} else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'...");
}
LOGGER.warn("generate the query: '{}'", query.toString());
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
if (parameters != null) {
for (final Object elem : parameters) {
@@ -1749,8 +1846,8 @@ public class DataAccess {
iii.inc();
}
condition.injectQuery(ps, iii);
if (limit != null) {
limit.injectQuery(ps, iii);
if (limits.size() == 1) {
limits.get(0).injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
@@ -1803,4 +1900,4 @@ public class DataAccess {
}
return outs;
}
}
}

View File

@@ -29,7 +29,8 @@ public interface DataAccessAddOn {
* @param iii The index of injection
* @return the new index of injection in case of multiple value management
* @throws SQLException */
void insertData(PreparedStatement ps, final Field field, Object data, CountInOut iii) throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
void insertData(PreparedStatement ps, final Field field, Object data, CountInOut iii)
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
/** Element can insert in the single request
* @param field
@@ -45,19 +46,24 @@ public interface DataAccessAddOn {
return false;
}
void generateQuery(//
@NotNull String tableName, //
@NotNull final String primaryKey, //
@NotNull Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull String name, //
@NotNull CountInOut count, //
QueryOptions options//
) throws Exception;
void generateQuery(
@NotNull String tableName,
@NotNull final String primaryKey,
@NotNull Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull String name,
@NotNull CountInOut count,
QueryOptions options) throws Exception;
// Return the number of colomn read
void fillFromQuery(ResultSet rs, Field field, Object data, CountInOut count, QueryOptions options, final List<LazyGetter> lazyCall)
void fillFromQuery(
ResultSet rs,
Field field,
Object data,
CountInOut count,
QueryOptions options,
final List<LazyGetter> lazyCall)
throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
/** Create associated table of the specific element.
@@ -69,17 +75,16 @@ public interface DataAccessAddOn {
* @param createDrop
* @param fieldId
* @throws Exception */
void createTables(//
String tableName, //
final Field primaryField, //
Field field, //
StringBuilder mainTableBuilder, //
List<String> preActionList, //
List<String> postActionList, //
boolean createIfNotExist, //
boolean createDrop, //
int fieldId //
) throws Exception;
void createTables(
String tableName,
final Field primaryField,
Field field,
StringBuilder mainTableBuilder,
List<String> preActionList,
List<String> postActionList,
boolean createIfNotExist,
boolean createDrop,
int fieldId) throws Exception;
/** Some action must be done asynchronously for update or remove element
* @param field
@@ -94,7 +99,12 @@ public interface DataAccessAddOn {
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
default void asyncInsert(final String tableName, final Object localId, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
default void asyncInsert(
final String tableName,
final Object localId,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
}
@@ -111,7 +121,12 @@ public interface DataAccessAddOn {
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
default void asyncUpdate(final String tableName, final Object localId, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
default void asyncUpdate(
final String tableName,
final Object localId,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
}

View File

@@ -20,6 +20,10 @@ import org.kar.archidata.dataAccess.exportTools.TableQuery;
import org.kar.archidata.dataAccess.exportTools.TableQueryTypes;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceOption;
import org.kar.archidata.dataAccess.options.GroupBy;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.DateTools;
@@ -37,7 +41,10 @@ public class DataExport {
public static final String CSV_TYPE = "text/csv";
@SuppressWarnings("unchecked")
protected static RetreiveFromDB createSetValueFromDbCallbackTable(final int count, final Class<?> type, final int id) throws Exception {
protected static RetreiveFromDB createSetValueFromDbCallbackTable(
final int count,
final Class<?> type,
final int id) throws Exception {
if (type == UUID.class) {
return (final ResultSet rs, final Object obj) -> {
final UUID tmp = rs.getObject(count, UUID.class);
@@ -228,7 +235,8 @@ public class DataExport {
}
private static int getQueryPropertyId(final List<TableQueryTypes> properties, final String name) throws DataAccessException {
private static int getQueryPropertyId(final List<TableQueryTypes> properties, final String name)
throws DataAccessException {
for (int iii = 0; iii < properties.size(); iii++) {
if (properties.get(iii).name.equals(name)) {
return iii;
@@ -237,42 +245,49 @@ public class DataExport {
throw new DataAccessException("Query with unknown field: '" + name + "'");
}
public static TableQuery queryTable(final List<TableQueryTypes> headers, final String query, final List<Object> parameters, final QueryOption... option) throws Exception {
public static TableQuery queryTable(
final List<TableQueryTypes> headers,
final String query,
final List<Object> parameters,
final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
return queryTable(headers, query, parameters, options);
}
public static TableQuery queryTable(final List<TableQueryTypes> headers, final String queryBase, final List<Object> parameters, final QueryOptions options) throws Exception {
public static TableQuery queryTable(
final List<TableQueryTypes> headers,
final String queryBase,
final List<Object> parameters,
final QueryOptions options) throws Exception {
final List<LazyGetter> lazyCall = new ArrayList<>();
// TODO ... final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
Condition condition = options.get(Condition.class);
if (condition == null) {
condition = new Condition();
}
final Condition condition = DataAccess.conditionFusionOrEmpty(options, false);
final StringBuilder query = new StringBuilder(queryBase);
final TableQuery out = new TableQuery(headers);
// real add in the BDD:
try {
final CountInOut count = new CountInOut();
condition.whereAppendQuery(query, null, options, null);
final GroupBy groups = options.get(GroupBy.class);
if (groups != null) {
groups.generateQuery(query, null);
final List<GroupBy> groups = options.get(GroupBy.class);
for (final GroupBy group : groups) {
group.generateQuery(query, null);
}
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuery(query, null);
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.generateQuery(query, null);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuery(query, null);
final List<Limit> limits = options.get(Limit.class);
if (limits.size() == 1) {
limits.get(0).generateQuery(query, null);
} else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'...");
}
LOGGER.warn("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
if (parameters != null) {
for (final Object elem : parameters) {
@@ -281,8 +296,8 @@ public class DataExport {
iii.inc();
}
condition.injectQuery(ps, iii);
if (limit != null) {
limit.injectQuery(ps, iii);
if (limits.size() == 1) {
limits.get(0).injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
@@ -294,7 +309,8 @@ public class DataExport {
final String typeName = rsmd.getColumnTypeName(jjj + 1);
final int typeId = rsmd.getColumnType(jjj + 1);
final int id = getQueryPropertyId(headers, label);
LOGGER.info(" - {}:{} type=[{}] {} REQUEST={}", jjj, label, typeId, typeName, headers.get(id).type.getCanonicalName());
LOGGER.info(" - {}:{} type=[{}] {} REQUEST={}", jjj, label, typeId, typeName,
headers.get(id).type.getCanonicalName());
// create the callback...
final RetreiveFromDB element = createSetValueFromDbCallbackTable(jjj + 1, headers.get(id).type, id);
actionToRetreive.add(element);
@@ -363,7 +379,8 @@ public class DataExport {
return out.toString();
}
public static Response convertInResponse(final TableQuery dataOut, final String accept) throws DataAccessException, IOException {
public static Response convertInResponse(final TableQuery dataOut, final String accept)
throws DataAccessException, IOException {
if (CSV_TYPE.equals(accept)) {
// CSV serialization
String out = null;

View File

@@ -142,8 +142,17 @@ public class DataFactory {
throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName());
}
public static void createTablesSpecificType(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List<String> preOtherTables, final List<String> postOtherTables,
final boolean createIfNotExist, final boolean createDrop, final int fieldId, final Class<?> classModel) throws Exception {
public static void createTablesSpecificType(
final String tableName,
final Field primaryField,
final Field elem,
final StringBuilder mainTableBuilder,
final List<String> preOtherTables,
final List<String> postOtherTables,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId,
final Class<?> classModel) throws Exception {
final String name = AnnotationTools.getFieldName(elem);
final int limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getColumnNotNull(elem);
@@ -217,10 +226,13 @@ public class DataFactory {
triggerBuilder.append(" SET ");
triggerBuilder.append(name);
// triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n");
final String tablePrimaryKey = primaryField.getName();
if ("varchar(33)".equals(typeValue)) {
triggerBuilder.append(" = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.id; \n");
triggerBuilder.append(" = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE " + tablePrimaryKey
+ " = NEW." + tablePrimaryKey + "; \n");
} else {
triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', 'now') WHERE id = NEW.id; \n");
triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', 'now') WHERE " + tablePrimaryKey
+ " = NEW." + tablePrimaryKey + "; \n");
}
triggerBuilder.append("END;");
@@ -351,6 +363,7 @@ public class DataFactory {
StringBuilder reverseOut = new StringBuilder();
final List<String> alreadyAdded = new ArrayList<>();
Class<?> currentClazz = clazz;
final Field tablePrimaryKeyField = AnnotationTools.getPrimaryKeyField(clazz);
while (currentClazz != null) {
fieldId = 0;
LOGGER.trace("parse class: '{}'", currentClazz.getCanonicalName());
@@ -372,16 +385,20 @@ public class DataFactory {
LOGGER.trace(" + '{}'", elem.getName());
if (DataAccess.isAddOnField(elem)) {
final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem);
LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType());
LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem),
elem.getType());
if (addOn != null) {
addOn.createTables(tableName, primaryField, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId);
addOn.createTables(tableName, primaryField, elem, tmpOut, preActionList, postActionList,
createIfNotExist, createDrop, fieldId);
} else {
throw new DataAccessException(
"Element matked as add-on but add-on does not loaded: table:" + tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType());
throw new DataAccessException("Element matked as add-on but add-on does not loaded: table:"
+ tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type="
+ elem.getType());
}
} else {
LOGGER.trace("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType());
DataFactory.createTablesSpecificType(tableName, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId, elem.getType());
DataFactory.createTablesSpecificType(tableName, tablePrimaryKeyField, elem, tmpOut, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, elem.getType());
}
fieldId++;
}

View File

@@ -1,653 +0,0 @@
package org.kar.archidata.dataAccess;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement;
import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
public class DataFactoryTsApi {
static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryTsApi.class);
record APIModel(String data, String className) {
}
/** Request the generation of the TypeScript file for the "Zod" export model
* @param classs List of class used in the model
* @throws Exception */
public static List<String> createApi(final List<Class<?>> classs, final GeneratedTypes previous, final String pathPackage) throws Exception {
final List<String> apis = new ArrayList<>();
final String globalheader = """
/**
* API of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
ModelResponseHttp,
RESTCallbacks,
RESTConfig,
RESTRequestJson,
RESTRequestJsonArray,
RESTRequestVoid
} from "./rest-tools"
import {""";
for (final Class<?> clazz : classs) {
final Set<Class<?>> includeModel = new HashSet<>();
final Set<Class<?>> includeCheckerModel = new HashSet<>();
final APIModel api = createSingleApi(clazz, includeModel, includeCheckerModel, previous);
final StringBuilder generatedData = new StringBuilder();
generatedData.append(globalheader);
for (final Class<?> elem : includeModel) {
if (elem == null) {
continue;
}
final ClassElement classElement = DataFactoryZod.createTable(elem, previous);
if (classElement.nativeType) {
continue;
}
generatedData.append("\n ");
generatedData.append(classElement.tsTypeName);
generatedData.append(",");
}
for (final Class<?> elem : includeCheckerModel) {
if (elem == null) {
continue;
}
final ClassElement classElement = DataFactoryZod.createTable(elem, previous);
if (classElement.nativeType) {
continue;
}
generatedData.append("\n ");
generatedData.append(classElement.tsCheckType);
generatedData.append(",");
}
generatedData.append("\n} from \"./model\"\n");
generatedData.append(api.data());
String fileName = api.className();
fileName = fileName.replaceAll("([A-Z])", "-$1").toLowerCase();
fileName = fileName.replaceAll("^\\-*", "");
apis.add(fileName);
final FileWriter myWriter = new FileWriter(pathPackage + File.separator + fileName + ".ts");
myWriter.write(generatedData.toString());
myWriter.close();
}
return apis;
}
public static String apiAnnotationGetPath(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static List<String> apiAnnotationProduces(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static List<String> apiAnnotationProduces(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static boolean apiAnnotationTypeScriptProgress(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(TypeScriptProgress.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static List<String> apiAnnotationProduces(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationProduces(method);
if (data != null) {
return data;
}
return apiAnnotationProduces(clazz);
}
public static String apiAnnotationGetOperationDescription(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Operation.class);
if (annotation.length == 0) {
return null;
}
return ((Operation) annotation[0]).description();
}
public static String apiAnnotationGetPath(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static String apiAnnotationGetTypeRequest(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return "GET";
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return "POST";
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return "PUT";
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return "PATCH";
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return "DELETE";
}
return null;
}
public static String apiAnnotationGetPathParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(PathParam.class);
if (annotation.length == 0) {
return null;
}
return ((PathParam) annotation[0]).value();
}
public static String apiAnnotationGetQueryParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(QueryParam.class);
if (annotation.length == 0) {
return null;
}
return ((QueryParam) annotation[0]).value();
}
public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class);
if (annotation.length == 0) {
return null;
}
return ((FormDataParam) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
}
public static List<String> apiAnnotationGetConsumes(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return apiAnnotationGetConsumes(clazz);
}
public static boolean apiAnnotationIsContext(final Parameter element) throws Exception {
return element.getDeclaredAnnotationsByType(Context.class).length != 0;
}
public static String convertInTypeScriptType(final List<ClassElement> tmp, final boolean isList) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsTypeName;
if (isList) {
out += "[]";
}
}
return out;
}
public static String convertInTypeScriptCheckType(final List<ClassElement> tmp) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsCheckType;
}
return out;
}
public static APIModel createSingleApi(final Class<?> clazz, final Set<Class<?>> includeModel, final Set<Class<?>> includeCheckerModel, final GeneratedTypes previous) throws Exception {
final StringBuilder builder = new StringBuilder();
// the basic path has no specific elements...
final String basicPath = apiAnnotationGetPath(clazz);
final String classSimpleName = clazz.getSimpleName();
builder.append("export namespace ");
builder.append(classSimpleName);
builder.append(" {\n");
LOGGER.info("Parse Class for path: {} => {}", classSimpleName, basicPath);
for (final Method method : clazz.getDeclaredMethods()) {
final String methodName = method.getName();
final String methodPath = apiAnnotationGetPath(method);
final String methodType = apiAnnotationGetTypeRequest(method);
if (methodType == null) {
LOGGER.error(" [{}] {} => {}/{} ==> No methode type @PATH, @GET ...", methodType, methodName, basicPath, methodPath);
continue;
}
final String methodDescription = apiAnnotationGetOperationDescription(method);
final List<String> consumes = apiAnnotationGetConsumes(clazz, method);
final List<String> produces = apiAnnotationProduces(clazz, method);
LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath);
if (methodDescription != null) {
LOGGER.trace(" description: {}", methodDescription);
}
final boolean needGenerateProgress = apiAnnotationTypeScriptProgress(method);
Class<?>[] returnTypeModel = apiAnnotationGetAsyncType(method);
boolean isUnmanagedReturnType = false;
boolean returnModelIsArray = false;
List<ClassElement> tmpReturn;
if (returnTypeModel == null) {
Class<?> returnTypeModelRaw = method.getReturnType();
LOGGER.info("Get type: {}", returnTypeModelRaw);
if (returnTypeModelRaw == Response.class) {
LOGGER.info("Get type: {}", returnTypeModelRaw);
}
if (returnTypeModelRaw == Response.class) {
isUnmanagedReturnType = true;
returnTypeModel = new Class<?>[] { Void.class };
tmpReturn = new ArrayList<>();
} else if (returnTypeModelRaw == List.class) {
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
returnTypeModelRaw = (Class<?>) listType.getActualTypeArguments()[0];
returnModelIsArray = true;
returnTypeModel = new Class<?>[] { returnTypeModelRaw };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
} else {
returnTypeModel = new Class<?>[] { returnTypeModelRaw };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
} else {
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
for (final ClassElement elem : tmpReturn) {
includeModel.add(elem.model[0]);
includeCheckerModel.add(elem.model[0]);
}
LOGGER.trace(" return: {}", tmpReturn.size());
for (final ClassElement elem : tmpReturn) {
LOGGER.trace(" - {}", elem.tsTypeName);
}
final Map<String, String> queryParams = new HashMap<>();
final Map<String, String> pathParams = new HashMap<>();
final Map<String, String> formDataParams = new HashMap<>();
final List<String> emptyElement = new ArrayList<>();
// LOGGER.info(" Parameters:");
for (final Parameter parameter : method.getParameters()) {
// Security context are internal parameter (not available from API)
if (apiAnnotationIsContext(parameter)) {
continue;
}
final Class<?> parameterType = parameter.getType();
String parameterTypeString;
final Class<?>[] asyncType = apiAnnotationGetAsyncType(parameter);
if (parameterType == List.class) {
if (asyncType == null) {
LOGGER.warn("Detect List param ==> not managed type ==> any[] !!!");
parameterTypeString = "any[]";
} else {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
}
parameterTypeString = convertInTypeScriptType(tmp, true);
}
} else if (asyncType == null) {
final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous);
includeModel.add(tmp.model[0]);
parameterTypeString = tmp.tsTypeName;
} else {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
}
parameterTypeString = convertInTypeScriptType(tmp, true);
}
final String pathParam = apiAnnotationGetPathParam(parameter);
final String queryParam = apiAnnotationGetQueryParam(parameter);
final String formDataParam = apiAnnotationGetFormDataParam(parameter);
if (queryParam != null) {
queryParams.put(queryParam, parameterTypeString);
} else if (pathParam != null) {
pathParams.put(pathParam, parameterTypeString);
} else if (formDataParam != null) {
formDataParams.put(formDataParam, parameterTypeString);
} else if (asyncType != null) {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
parameterTypeString = "";
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
if (parameterTypeString.length() != 0) {
parameterTypeString += " | ";
}
parameterTypeString += elem.tsTypeName;
}
emptyElement.add(parameterTypeString);
} else if (parameterType == List.class) {
parameterTypeString = "any[]";
final Class<?> plop = parameterType.arrayType();
LOGGER.info("ArrayType = {}", plop);
emptyElement.add(parameterTypeString);
} else {
final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous);
includeModel.add(tmp.model[0]);
emptyElement.add(tmp.tsTypeName);
}
}
if (!queryParams.isEmpty()) {
LOGGER.trace(" Query parameter:");
for (final Entry<String, String> queryEntry : queryParams.entrySet()) {
LOGGER.trace(" - {}: {}", queryEntry.getKey(), queryEntry.getValue());
}
}
if (!pathParams.isEmpty()) {
LOGGER.trace(" Path parameter:");
for (final Entry<String, String> pathEntry : pathParams.entrySet()) {
LOGGER.trace(" - {}: {}", pathEntry.getKey(), pathEntry.getValue());
}
}
if (emptyElement.size() > 1) {
LOGGER.error(" Fail to parse: Too much element in the model for the data ...");
continue;
} else if (emptyElement.size() == 1 && formDataParams.size() != 0) {
LOGGER.error(" Fail to parse: Incompatible form data & direct data ...");
continue;
} else if (emptyElement.size() == 1) {
LOGGER.trace(" data type: {}", emptyElement.get(0));
}
// ALL is good can generate the Elements
if (methodDescription != null) {
builder.append("\n\t/**\n\t * ");
builder.append(methodDescription);
builder.append("\n\t */");
}
if (isUnmanagedReturnType) {
builder.append("\n\t// TODO: unmanaged \"Response\" type: please specify @AsyncType or considered as 'void'.");
}
builder.append("\n\texport function ");
builder.append(methodName);
builder.append("({\n\t\t\trestConfig,");
if (!queryParams.isEmpty()) {
builder.append("\n\t\t\tqueries,");
}
if (!pathParams.isEmpty()) {
builder.append("\n\t\t\tparams,");
}
if (produces.size() > 1) {
builder.append("\n\t\t\tproduce,");
}
if (emptyElement.size() == 1 || formDataParams.size() != 0) {
builder.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
builder.append("\n\t\t\tcallback,");
}
builder.append("\n\t\t}: {");
builder.append("\n\t\trestConfig: RESTConfig,");
if (!queryParams.isEmpty()) {
builder.append("\n\t\tqueries: {");
for (final Entry<String, String> queryEntry : queryParams.entrySet()) {
builder.append("\n\t\t\t");
builder.append(queryEntry.getKey());
builder.append("?: ");
builder.append(queryEntry.getValue());
builder.append(",");
}
builder.append("\n\t\t},");
}
if (!pathParams.isEmpty()) {
builder.append("\n\t\tparams: {");
for (final Entry<String, String> pathEntry : pathParams.entrySet()) {
builder.append("\n\t\t\t");
builder.append(pathEntry.getKey());
builder.append(": ");
builder.append(pathEntry.getValue());
builder.append(",");
}
builder.append("\n\t\t},");
}
if (emptyElement.size() == 1) {
builder.append("\n\t\tdata: ");
builder.append(emptyElement.get(0));
builder.append(",");
} else if (formDataParams.size() != 0) {
builder.append("\n\t\tdata: {");
for (final Entry<String, String> pathEntry : formDataParams.entrySet()) {
builder.append("\n\t\t\t");
builder.append(pathEntry.getKey());
builder.append(": ");
builder.append(pathEntry.getValue());
builder.append(",");
}
builder.append("\n\t\t},");
}
if (produces.size() > 1) {
builder.append("\n\t\tproduce: ");
String isFist = null;
for (final String elem : produces) {
String lastElement = null;
if (MediaType.APPLICATION_JSON.equals(elem)) {
lastElement = "HTTPMimeType.JSON";
}
if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
lastElement = "HTTPMimeType.MULTIPART";
}
if (DataExport.CSV_TYPE.equals(elem)) {
lastElement = "HTTPMimeType.CSV";
}
if (lastElement != null) {
if (isFist == null) {
isFist = lastElement;
} else {
builder.append(" | ");
}
builder.append(lastElement);
} else {
LOGGER.error("Unmanaged model type: {}", elem);
}
}
builder.append(",");
}
if (needGenerateProgress) {
builder.append("\n\t\tcallback?: RESTCallbacks,");
}
builder.append("\n\t}): Promise<");
if (tmpReturn.size() == 0 //
|| tmpReturn.get(0).tsTypeName == null //
|| tmpReturn.get(0).tsTypeName.equals("void")) {
builder.append("void");
} else {
builder.append(convertInTypeScriptType(tmpReturn, returnModelIsArray));
}
builder.append("> {");
if (tmpReturn.size() == 0 //
|| tmpReturn.get(0).tsTypeName == null //
|| tmpReturn.get(0).tsTypeName.equals("void")) {
builder.append("\n\t\treturn RESTRequestVoid({");
} else if (returnModelIsArray) {
builder.append("\n\t\treturn RESTRequestJsonArray({");
} else {
builder.append("\n\t\treturn RESTRequestJson({");
}
builder.append("\n\t\t\trestModel: {");
builder.append("\n\t\t\t\tendPoint: \"");
builder.append(basicPath);
if (methodPath != null) {
builder.append("/");
builder.append(methodPath);
}
builder.append("\",");
builder.append("\n\t\t\t\trequestType: HTTPRequestModel.");
builder.append(methodType);
builder.append(",");
if (consumes != null) {
for (final String elem : consumes) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
builder.append("\n\t\t\t\tcontentType: HTTPMimeType.JSON,");
break;
} else if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
builder.append("\n\t\t\t\tcontentType: HTTPMimeType.MULTIPART,");
break;
} else if (MediaType.TEXT_PLAIN.equals(elem)) {
builder.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
break;
}
}
} else if ("DELETE".equals(methodType)) {
builder.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
}
if (produces != null) {
if (produces.size() > 1) {
builder.append("\n\t\t\t\taccept: produce,");
} else {
for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
builder.append("\n\t\t\t\taccept: HTTPMimeType.JSON,");
break;
}
}
}
}
builder.append("\n\t\t\t},");
builder.append("\n\t\t\trestConfig,");
if (!pathParams.isEmpty()) {
builder.append("\n\t\t\tparams,");
}
if (!queryParams.isEmpty()) {
builder.append("\n\t\t\tqueries,");
}
if (emptyElement.size() == 1) {
builder.append("\n\t\t\tdata,");
} else if (formDataParams.size() != 0) {
builder.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
builder.append("\n\t\t\tcallback,");
}
builder.append("\n\t\t}");
if (tmpReturn.size() != 0 && tmpReturn.get(0).tsTypeName != null && !tmpReturn.get(0).tsTypeName.equals("void")) {
builder.append(", ");
// TODO: correct this it is really bad ...
builder.append(convertInTypeScriptCheckType(tmpReturn));
}
builder.append(");");
builder.append("\n\t};");
}
builder.append("\n}\n");
return new APIModel(builder.toString(), classSimpleName);
}
public static void generatePackage(final List<Class<?>> classApi, final List<Class<?>> classModel, final String pathPackage) throws Exception {
final GeneratedTypes previous = DataFactoryZod.createBasicType();
DataFactoryZod.createTable(RestErrorResponse.class, previous);
final List<String> listApi = createApi(classApi, previous, pathPackage);
final String packageApi = DataFactoryZod.createTables(new ArrayList<>(classModel), previous);
FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts");
myWriter.write(packageApi.toString());
myWriter.close();
final StringBuilder index = new StringBuilder("""
/**
* Global import of the package
*/
export * from "./model";
""");
for (final String api : listApi) {
index.append("export * from \"./").append(api).append("\";\n");
}
myWriter = new FileWriter(pathPackage + File.separator + "index.ts");
myWriter.write(index.toString());
myWriter.close();
final InputStream ioStream = DataFactoryTsApi.class.getClassLoader().getResourceAsStream("rest-tools.ts");
if (ioStream == null) {
throw new IllegalArgumentException("rest-tools.ts is not found");
}
final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream));
myWriter = new FileWriter(pathPackage + File.separator + "rest-tools.ts");
String line;
while ((line = buffer.readLine()) != null) {
myWriter.write(line);
myWriter.write("\n");
}
ioStream.close();
myWriter.close();
return;
}
}

View File

@@ -1,461 +0,0 @@
package org.kar.archidata.dataAccess;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response;
public class DataFactoryZod {
static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryZod.class);
static public class ClassElement {
public Class<?>[] model;
public String zodName;
public String tsTypeName;
public String tsCheckType;
public String declaration;
public String comment = null;
public boolean isEnum = false;
public boolean nativeType;
public ClassElement(final Class<?> model[], final String zodName, final String tsTypeName, final String tsCheckType, final String declaration, final boolean nativeType) {
this.model = model;
this.zodName = zodName;
this.tsTypeName = tsTypeName;
this.tsCheckType = tsCheckType;
this.declaration = declaration;
this.nativeType = nativeType;
}
public ClassElement(final Class<?> model) {
this(new Class<?>[] { model });
}
public ClassElement(final Class<?> model[]) {
this.model = model;
this.zodName = "Zod" + model[0].getSimpleName();
this.tsTypeName = model[0].getSimpleName();
this.tsCheckType = "is" + model[0].getSimpleName();
this.declaration = null;
this.nativeType = false;
}
}
public static class GeneratedTypes {
final List<ClassElement> previousGeneration = new ArrayList<>();
final List<Class<?>> order = new ArrayList<>();
public ClassElement find(final Class<?> clazz) {
for (final ClassElement elem : this.previousGeneration) {
for (final Class<?> elemClass : elem.model) {
if (elemClass == clazz) {
return elem;
}
}
}
return null;
}
public void add(final ClassElement elem) {
this.previousGeneration.add(elem);
}
public void add(final ClassElement elem, final boolean addOrder) {
this.previousGeneration.add(elem);
if (addOrder) {
this.order.add(elem.model[0]);
}
}
public void addOrder(final ClassElement elem) {
this.order.add(elem.model[0]);
}
}
public static ClassElement convertTypeZodEnum(final Class<?> clazz, final GeneratedTypes previous) throws Exception {
final ClassElement element = new ClassElement(clazz);
previous.add(element);
final Object[] arr = clazz.getEnumConstants();
final StringBuilder out = new StringBuilder();
if (System.getenv("ARCHIDATA_GENERATE_ZOD_ENUM") != null) {
boolean first = true;
out.append("zod.enum([");
for (final Object elem : arr) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append("'");
out.append(elem.toString());
out.append("'");
}
if (first) {
out.append("]}");
} else {
out.append("\n\t])");
}
} else {
element.isEnum = true;
boolean first = true;
out.append("{");
for (final Object elem : arr) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append(elem.toString());
out.append(" = '");
out.append(elem.toString());
out.append("'");
}
if (first) {
out.append("}");
} else {
out.append(",\n\t}");
}
}
element.declaration = out.toString();
previous.addOrder(element);
return element;
}
public static String convertTypeZod(final Class<?> type, final GeneratedTypes previous) throws Exception {
final ClassElement previousType = previous.find(type);
if (previousType != null) {
return previousType.zodName;
}
if (type.isEnum()) {
return convertTypeZodEnum(type, previous).zodName;
}
if (type == List.class) {
throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName() + " Unmanaged List of List ... ");
}
final ClassElement elemCreated = createTable(type, previous);
if (elemCreated != null) {
return elemCreated.zodName;
}
throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName());
}
public static String convertTypeZod(final Field field, final GeneratedTypes previous) throws Exception {
final Class<?> type = field.getType();
final ClassElement previousType = previous.find(type);
if (previousType != null) {
return previousType.zodName;
}
if (type.isEnum()) {
return convertTypeZodEnum(type, previous).zodName;
}
if (type == List.class) {
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
final String simpleSubType = convertTypeZod(listClass, previous);
return "zod.array(" + simpleSubType + ")";
}
final ClassElement elemCreated = createTable(type, previous);
if (elemCreated != null) {
return elemCreated.zodName;
}
throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName());
}
public static String optionalTypeZod(final Class<?> type) throws Exception {
if (type.isPrimitive()) {
return "";
}
return ".optional()";
}
public static void createTablesSpecificType(final Field elem, final int fieldId, final StringBuilder builder, final GeneratedTypes previous) throws Exception {
final String name = elem.getName();
final Class<?> classModel = elem.getType();
final int limitSize = AnnotationTools.getLimitSize(elem);
final String comment = AnnotationTools.getComment(elem);
if (fieldId != 0) {
builder.append(",");
}
if (comment != null) {
builder.append("\n\t// ");
builder.append(comment);
}
builder.append("\n\t");
builder.append(name);
builder.append(": ");
builder.append(convertTypeZod(elem, previous));
if (limitSize > 0 && classModel == String.class) {
builder.append(".max(");
builder.append(limitSize);
builder.append(")");
}
if (AnnotationTools.getSchemaReadOnly(elem)) {
builder.append(".readonly()");
}
builder.append(optionalTypeZod(classModel));
}
private static boolean isFieldFromSuperClass(final Class<?> model, final String filedName) {
final Class<?> superClass = model.getSuperclass();
if (superClass == null) {
return false;
}
for (final Field field : superClass.getFields()) {
String name;
try {
name = AnnotationTools.getFieldName(field);
if (filedName.equals(name)) {
return true;
}
} catch (final Exception e) {
// TODO Auto-generated catch block
LOGGER.trace("Catch error field name in parent create data table: {}", e.getMessage());
}
}
return false;
}
public static GeneratedTypes createBasicType() throws Exception {
final GeneratedTypes previous = new GeneratedTypes();
previous.add(new ClassElement(new Class<?>[] { Void.class, void.class }, "void", "void", null, null, true));
previous.add(new ClassElement(new Class<?>[] { String.class }, "zod.string()", "string", null, "zod.string()", true));
previous.add(new ClassElement(new Class<?>[] { InputStream.class, FormDataContentDisposition.class }, "z.instanceof(File)", "File", null, "z.instanceof(File)", true));
previous.add(new ClassElement(new Class<?>[] { Boolean.class, boolean.class }, "zod.boolean()", "boolean", null, "zod.boolean()", true));
previous.add(new ClassElement(new Class<?>[] { UUID.class }, "ZodUUID", "UUID", "isUUID", "zod.string().uuid()", false), true);
previous.add(new ClassElement(new Class<?>[] { Long.class, long.class }, "ZodLong", "Long", "isLong",
// "zod.bigint()",
"zod.number()", false), true);
previous.add(new ClassElement(new Class<?>[] { Integer.class, int.class }, "ZodInteger", "Integer", "isInteger", "zod.number().safe()", false), true);
previous.add(new ClassElement(new Class<?>[] { Double.class, double.class }, "ZodDouble", "Double", "isDouble", "zod.number()", true), true);
previous.add(new ClassElement(new Class<?>[] { Float.class, float.class }, "ZodFloat", "Float", "isFloat", "zod.number()", false), true);
previous.add(new ClassElement(new Class<?>[] { Instant.class }, "ZodInstant", "Instant", "isInstant", "zod.string()", false), true);
previous.add(new ClassElement(new Class<?>[] { Date.class }, "ZodDate", "Date", "isDate", "zod.date()", false), true);
previous.add(new ClassElement(new Class<?>[] { Timestamp.class }, "ZodTimestamp", "Timestamp", "isTimestamp", "zod.date()", false), true);
previous.add(new ClassElement(new Class<?>[] { LocalDate.class }, "ZodLocalDate", "LocalDate", "isLocalDate", "zod.date()", false), true);
previous.add(new ClassElement(new Class<?>[] { LocalTime.class }, "ZodLocalTime", "LocalTime", "isLocalTime", "zod.date()", false), true);
return previous;
}
/** Request the generation of the TypeScript file for the "Zod" export model
* @param classs List of class used in the model
* @return A string representing the Server models
* @throws Exception */
public static String createTables(final List<Class<?>> classs) throws Exception {
return createTables(classs, createBasicType());
}
public static String createTables(final List<Class<?>> classs, final GeneratedTypes previous) throws Exception {
for (final Class<?> clazz : classs) {
createTable(clazz, previous);
}
final StringBuilder generatedData = new StringBuilder();
generatedData.append("""
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from \"zod\";
""");
for (final Class<?> elem : previous.order) {
final ClassElement data = previous.find(elem);
if (!data.nativeType) {
if (data.comment != null) {
generatedData.append(data.comment);
}
generatedData.append(createDeclaration(data));
generatedData.append("\n\n");
}
}
LOGGER.info("generated: {}", generatedData.toString());
return generatedData.toString();
}
public static List<ClassElement> createTables(final Class<?>[] classs, final GeneratedTypes previous) throws Exception {
final List<ClassElement> out = new ArrayList<>();
for (final Class<?> clazz : classs) {
if (clazz == Response.class) {
throw new IOException("Can not generate a Zod element for an unknow type Response");
}
out.add(createTable(clazz, previous));
}
return out;
}
public static ClassElement createTable(final Class<?> clazz, final GeneratedTypes previous) throws Exception {
if (clazz == null) {
return null;
}
if (clazz == Response.class) {
throw new IOException("Can not generate a Zod element for an unknow type Response");
}
final ClassElement alreadyExist = previous.find(clazz);
if (previous.find(clazz) != null) {
return alreadyExist;
}
if (clazz.isPrimitive()) {
return null;
}
// add the current class to prevent multiple creation
final ClassElement curentElementClass = new ClassElement(clazz);
previous.add(curentElementClass);
// Local generation of class:
final StringBuilder internalBuilder = new StringBuilder();
final List<String> alreadyAdded = new ArrayList<>();
LOGGER.trace("parse class: '{}'", clazz.getCanonicalName());
int fieldId = 0;
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
continue;
}
final String dataName = elem.getName();
if (isFieldFromSuperClass(clazz, dataName)) {
LOGGER.trace(" SKIP: '{}'", elem.getName());
continue;
}
if (alreadyAdded.contains(dataName)) {
LOGGER.trace(" SKIP2: '{}'", elem.getName());
continue;
}
alreadyAdded.add(dataName);
LOGGER.trace(" + '{}'", elem.getName());
if (false && DataAccess.isAddOnField(elem)) {
final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem);
LOGGER.error("Create type for: {} ==> {} (ADD-ON) ==> Not managed now ....", AnnotationTools.getFieldName(elem), elem.getType());
/* LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType()); if (addOn != null) { addOn.createTables(tableName, elem, tmpOut,
* preActionList, postActionList, createIfNotExist, createDrop, fieldId); } else { throw new DataAccessException( "Element matked as add-on but add-on does not loaded: table:" +
* tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType()); } fieldId++; */
} else {
LOGGER.trace("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType());
DataFactoryZod.createTablesSpecificType(elem, fieldId, internalBuilder, previous);
fieldId++;
}
}
final String description = AnnotationTools.getSchemaDescription(clazz);
final String example = AnnotationTools.getSchemaExample(clazz);
final StringBuilder generatedCommentedData = new StringBuilder();
if (description != null || example != null) {
generatedCommentedData.append("/**\n");
if (description != null) {
for (final String elem : description.split("\n")) {
generatedCommentedData.append(" * ");
generatedCommentedData.append(elem);
generatedCommentedData.append("\n");
}
}
if (example != null) {
generatedCommentedData.append(" * Example:\n");
generatedCommentedData.append(" * ```\n");
for (final String elem : example.split("\n")) {
generatedCommentedData.append(" * ");
generatedCommentedData.append(elem);
generatedCommentedData.append("\n");
}
generatedCommentedData.append(" * ```\n");
}
generatedCommentedData.append(" */\n");
}
curentElementClass.comment = generatedCommentedData.toString();
final StringBuilder generatedData = new StringBuilder();
final Class<?> parentClass = clazz.getSuperclass();
if (parentClass != null && parentClass != Object.class && parentClass != Record.class) {
final ClassElement parentDeclaration = createTable(parentClass, previous);
generatedData.append(parentDeclaration.zodName);
generatedData.append(".extend({");
} else {
generatedData.append("zod.object({");
}
generatedData.append(internalBuilder.toString());
generatedData.append("\n})");
// Remove the previous to reorder the map ==> parent must be inserted before us.
curentElementClass.declaration = generatedData.toString();
previous.addOrder(curentElementClass);
return curentElementClass;
}
public static String createDeclaration(final ClassElement elem) {
final StringBuilder generatedData = new StringBuilder();
if (elem.isEnum) {
generatedData.append("export enum ");
generatedData.append(elem.tsTypeName);
generatedData.append(" ");
generatedData.append(elem.declaration);
generatedData.append(";");
generatedData.append("\nexport const ");
generatedData.append(elem.zodName);
generatedData.append(" = zod.nativeEnum(");
generatedData.append(elem.tsTypeName);
generatedData.append(");");
} else {
generatedData.append("export const ");
generatedData.append(elem.zodName);
generatedData.append(" = ");
generatedData.append(elem.declaration);
generatedData.append(";");
generatedData.append("\nexport type ");
generatedData.append(elem.tsTypeName);
generatedData.append(" = zod.infer<typeof ");
generatedData.append(elem.zodName);
generatedData.append(">;");
}
// declare generic isXXX:
generatedData.append("\nexport function ");
generatedData.append(elem.tsCheckType);
generatedData.append("(data: any): data is ");
generatedData.append(elem.tsTypeName);
generatedData.append(" {\n\ttry {\n\t\t");
generatedData.append(elem.zodName);
generatedData.append("""
.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
""");
return generatedData.toString();
}
public static void generatePackage(final List<Class<?>> classs, final String pathPackage) throws Exception {
final String packageApi = createTables(classs);
FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts");
myWriter.write(packageApi.toString());
myWriter.close();
final String index = """
/**
* Global import of the package
*/
export * from "./model.ts";
""";
myWriter = new FileWriter(pathPackage + File.separator + "index.ts");
myWriter.write(index);
myWriter.close();
}
}

View File

@@ -8,17 +8,17 @@ import java.util.List;
public class QueryAnd implements QueryItem {
protected final List<QueryItem> childs;
public QueryAnd(final List<QueryItem> childs) {
this.childs = childs;
public QueryAnd(final List<QueryItem> child) {
this.childs = child;
}
public QueryAnd(final QueryItem... items) {
public QueryAnd(final QueryItem... child) {
this.childs = new ArrayList<>();
Collections.addAll(this.childs, items);
Collections.addAll(this.childs, child);
}
public void add(final QueryItem... items) {
Collections.addAll(this.childs, items);
public void add(final QueryItem... child) {
Collections.addAll(this.childs, child);
}
@Override

View File

@@ -7,6 +7,12 @@ public class QueryCondition implements QueryItem {
private final String comparator;
private final Object value;
/**
* Simple DB comparison element. Note the injected object is injected in the statement and not in the query directly.
* @param key Field to check (the Model property name)
* @param comparator (simple comparator String)
* @param value Value that the field must be equals.
*/
public QueryCondition(final String key, final String comparator, final Object value) {
this.key = key;
this.comparator = comparator;

View File

@@ -1,3 +0,0 @@
package org.kar.archidata.dataAccess;
public class QueryOption {}

View File

@@ -1,11 +1,11 @@
package org.kar.archidata.dataAccess;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.CreateDropTable;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.dataAccess.options.ReadAllColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,10 +24,16 @@ public class QueryOptions {
if (elems == null || elems.length == 0) {
return;
}
Collections.addAll(this.options, elems);
for (final QueryOption elem : elems) {
add(elem);
}
}
public void add(final QueryOption option) {
if (option == null) {
return;
}
this.options.add(option);
}
@@ -40,13 +46,14 @@ public class QueryOptions {
}
@SuppressWarnings("unchecked")
public <T> T get(final Class<T> type) {
public <T> List<T> get(final Class<T> type) {
final List<T> out = new ArrayList<>();
for (final QueryOption elem : this.options) {
if (elem.getClass() == type) {
return (T) elem;
out.add((T) elem);
}
}
return null;
return out;
}
public boolean exist(final Class<?> type) {

View File

@@ -4,6 +4,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
@@ -26,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -52,7 +54,8 @@ public class AddOnDataJson implements DataAccessAddOn {
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception {
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws IllegalArgumentException, IllegalAccessException, SQLException, JsonProcessingException {
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
@@ -69,7 +72,7 @@ public class AddOnDataJson implements DataAccessAddOn {
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
public boolean isInsertAsync(final Field field) {
return false;
}
@@ -79,16 +82,15 @@ public class AddOnDataJson implements DataAccessAddOn {
}
@Override
public void generateQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) throws Exception {
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
@@ -98,7 +100,13 @@ public class AddOnDataJson implements DataAccessAddOn {
}
@Override
public void fillFromQuery(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall) throws Exception {
public void fillFromQuery(
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
final String jsonData = rs.getString(count.value);
count.inc();
if (!rs.wasNull()) {
@@ -149,23 +157,25 @@ public class AddOnDataJson implements DataAccessAddOn {
}
@Override
public void createTables(//
final String tableName, //
final Field primaryField, //
final Field field, //
final StringBuilder mainTableBuilder, //
final List<String> preActionList, //
final List<String> postActionList, //
final boolean createIfNotExist, //
final boolean createDrop, //
final int fieldId //
) throws Exception {
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class);
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId) throws Exception {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class);
}
public static void addLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception {
public static void addLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
@@ -178,9 +188,23 @@ public class AddOnDataJson implements DataAccessAddOn {
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
public static void addLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception {
/**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName));
// TODO: Get primary key name
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
@@ -190,12 +214,25 @@ public class AddOnDataJson implements DataAccessAddOn {
}
}
data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));// TODO: ,new OverrideFieldName("covers", column));
}
public static void addLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception {
/**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(final Class<?> clazz, final UUID uuid, final String column, final UUID remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName));
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
@@ -205,12 +242,14 @@ public class AddOnDataJson implements DataAccessAddOn {
}
}
data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
}
public static void addLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception {
public static void addLink(final Class<?> clazz, final UUID uuid, final String column, final Long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName));
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
@@ -220,12 +259,52 @@ public class AddOnDataJson implements DataAccessAddOn {
}
}
data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception {
public static void removeLink(final Class<?> clazz, final UUID uuid, final String column, final Long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName));
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<Long> newList = new ArrayList<>();
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final UUID uuid, final String column, final UUID remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<UUID> newList = new ArrayList<>();
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
@@ -240,9 +319,11 @@ public class AddOnDataJson implements DataAccessAddOn {
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception {
public static void removeLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName));
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
@@ -256,43 +337,4 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<Long> newList = new ArrayList<>();
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
public static void removeLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName));
if (data.covers == null) {
return;
}
final List<UUID> newList = new ArrayList<>();
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
/* public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { final String tableName =
* AnnotationTools.getTableName(clazz); final TYPE data = DataAccess.get(clazz, localKey); // TODO: add filter of the "column" // find the field column: // add the remoteKey in the list: // post
* new data in the DB final String linkTableName = generateLinkTableName(this.tableName, this.column); final LinkTable insertElement = new LinkTable(this.localKey, this.remoteKey);
* DataAccess.insert(insertElement, new OverrideTableName(linkTableName)); } */
}

View File

@@ -56,7 +56,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws SQLException, IllegalArgumentException, IllegalAccessException {
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException {
}
@@ -70,7 +71,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
if (field.getType() != List.class) {
return false;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
return true;
}
@@ -97,18 +99,18 @@ public class AddOnManyToMany implements DataAccessAddOn {
return tableName + "_link_" + localName;
}
public void generateConcatQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) throws Exception {
public void generateConcatQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
final String linkTableName = generateLinkTableName(tableName, name);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String tmpVariable = "tmp_" + Integer.toString(count.value);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpVariable);
@@ -132,7 +134,9 @@ public class AddOnManyToMany implements DataAccessAddOn {
querySelect.append(" ");
querySelect.append(tmpVariable);
querySelect.append(" WHERE ");
/* querySelect.append(tmpVariable); querySelect.append(".deleted = false AND "); */
querySelect.append(tmpVariable);
querySelect.append(".deleted = false");
querySelect.append(" AND ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
@@ -154,20 +158,21 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
@Override
public void generateQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) throws Exception {
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
@@ -185,19 +190,19 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
@Override
public void fillFromQuery( //
final ResultSet rs, //
final Field field, //
final Object data, //
final CountInOut count, //
final QueryOptions options, //
final List<LazyGetter> lazyCall //
) throws Exception {
public void fillFromQuery(
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class) {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
@@ -228,7 +233,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
final List<Long> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(), new Condition(new QueryInList<>(idField, childs)));
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
@@ -247,7 +253,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
final List<UUID> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(), new Condition(new QueryInList<>(idField, childs)));
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
@@ -265,14 +272,21 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
@Override
public void asyncUpdate(final String tableName, final Object localKey, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
public void asyncUpdate(
final String tableName,
final Object localKey,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">");
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
@@ -280,24 +294,28 @@ public class AddOnManyToMany implements DataAccessAddOn {
if (localKey instanceof final Long localKeyLong) {
if (objectClass == Long.class) {
actions.add(() -> {
DataAccess.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
DataAccess.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
});
asyncInsert(tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
DataAccess.deleteWhere(LinkTableLongUUID.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
DataAccess.deleteWhere(LinkTableLongUUID.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyLong)));
});
asyncInsert(tableName, localKey, field, data, actions);
}
} else if (localKey instanceof final UUID localKeyUUID) {
if (objectClass == Long.class) {
actions.add(() -> {
DataAccess.deleteWhere(LinkTableUUIDLong.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
DataAccess.deleteWhere(LinkTableUUIDLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
});
asyncInsert(tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
DataAccess.deleteWhere(LinkTableUUIDUUID.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
DataAccess.deleteWhere(LinkTableUUIDUUID.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
});
asyncInsert(tableName, localKey, field, data, actions);
}
@@ -310,7 +328,12 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
@Override
public void asyncInsert(final String tableName, final Object localKey, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
public void asyncInsert(
final String tableName,
final Object localKey,
final Field field,
final Object data,
final List<LazyGetter> actions) throws Exception {
if (data == null) {
return;
}
@@ -318,9 +341,11 @@ public class AddOnManyToMany implements DataAccessAddOn {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">");
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
@@ -421,7 +446,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
});
}
} else {
throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long or UUID: " + localKey.getClass().getCanonicalName());
throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long or UUID: "
+ localKey.getClass().getCanonicalName());
}
}
@@ -430,9 +456,11 @@ public class AddOnManyToMany implements DataAccessAddOn {
public void drop(final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">");
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
DataAccess.drop(LinkTableLongLong.class, new OverrideTableName(linkTableName));
}
@@ -441,14 +469,17 @@ public class AddOnManyToMany implements DataAccessAddOn {
public void cleanAll(final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">");
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
DataAccess.cleanAll(LinkTableLongLong.class, new OverrideTableName(linkTableName));
}
public static void addLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
public static void addLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
/* final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (objectClass != Long.class && objectClass != UUID.class) { throw new
@@ -458,30 +489,33 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
public static int removeLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
public static int removeLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
return DataAccess.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName),
new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey))));
new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey),
new QueryCondition("object2Id", "=", remoteKey))));
}
@Override
public void createTables( //
final String tableName, //
final Field primaryField, //
final Field field, //
final StringBuilder mainTableBuilder, //
final List<String> preActionList, //
final List<String> postActionList, //
final boolean createIfNotExist, //
final boolean createDrop, //
final int fieldId//
) throws Exception {
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId) throws Exception {
final String linkTableName = generateLinkTableNameField(tableName, field);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">");
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final Class<?> primaryType = primaryField.getType();
if (primaryType == Long.class) {
@@ -502,7 +536,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
postActionList.addAll(sqlCommand);
}
} else {
throw new DataAccessException("Can not ManyToMany with other than primary key type Long or UUID Model: " + primaryType.getCanonicalName());
throw new DataAccessException("Can not ManyToMany with other than primary key type Long or UUID Model: "
+ primaryType.getCanonicalName());
}
}
}

View File

@@ -48,7 +48,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception {
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws Exception {
final Object data = field.get(rootObject);
if (data == null) {
if (field.getType() == Long.class) {
@@ -95,11 +96,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean canInsert(final Field field) {
if (field.getType() == Long.class //
|| field.getType() == Integer.class //
|| field.getType() == Short.class //
|| field.getType() == String.class //
|| field.getType() == UUID.class) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -117,11 +115,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType();
if (classType == Long.class //
|| classType == Integer.class //
|| classType == Short.class //
|| classType == String.class //
|| classType == UUID.class) {
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -132,21 +127,17 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public void generateQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) throws Exception {
if (field.getType() == Long.class //
|| field.getType() == Integer.class //
|| field.getType() == Short.class //
|| field.getType() == String.class //
|| field.getType() == UUID.class) {
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
@@ -186,7 +177,13 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public void fillFromQuery(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall) throws Exception {
public void fillFromQuery(
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() == Long.class) {
final Long foreignKey = rs.getLong(count.value);
count.inc();
@@ -236,11 +233,16 @@ public class AddOnManyToOne implements DataAccessAddOn {
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
final CountInOut countNotNull = new CountInOut(0);
final Object dataNew = DataAccess.createObjectFromSQLRequest(rs, objectClass, count, countNotNull, options, lazyCall);
final Object dataNew = DataAccess.createObjectFromSQLRequest(rs, objectClass, count, countNotNull,
options, lazyCall);
if (dataNew != null && countNotNull.value != 0) {
field.set(data, dataNew);
}
} else {
return;
}
final Field remotePrimaryKeyField = AnnotationTools.getFieldOfId(objectClass);
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
if (remotePrimaryKeyType == Long.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final Long foreignKey = rs.getLong(count.value);
count.inc();
@@ -256,33 +258,47 @@ public class AddOnManyToOne implements DataAccessAddOn {
};
lazyCall.add(lambda);
}
} else if (remotePrimaryKeyType == UUID.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final UUID foreignKey = DataAccess.getListOfRawUUID(rs, count.value);
count.inc();
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = DataAccess.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serialisable with the default system
@Override
public void createTables(//
final String tableName, //
final Field primaryField, //
final Field field, //
final StringBuilder mainTableBuilder, //
final List<String> preActionList, //
final List<String> postActionList, //
final boolean createIfNotExist, //
final boolean createDrop, //
final int fieldId //
) throws Exception {
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId) throws Exception {
final Class<?> classType = field.getType();
if (classType == Long.class //
|| classType == Integer.class //
|| classType == Short.class //
|| classType == String.class //
|| classType == UUID.class) {
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, classType);
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType);
} else {
LOGGER.error("Support only the Long remote field of ecternal primary keys...");
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, Long.class);
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, Long.class);
}
}
}

View File

@@ -1,28 +1,36 @@
package org.kar.archidata.dataAccess.addOn;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
static final String SEPARATOR_LONG = "-";
/** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed)
@@ -75,16 +83,9 @@ public class AddOnOneToMany implements DataAccessAddOn {
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws SQLException, IllegalArgumentException, IllegalAccessException {
final Object data = field.get(rootObject);
iii.inc();
if (data == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
@SuppressWarnings("unchecked")
final String dataTmp = getStringOfIds((List<Long>) data);
ps.setString(iii.value, dataTmp);
}
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException {
throw new IllegalAccessException("Can not generate an inset of @OneToMany");
}
@Override
@@ -94,56 +95,226 @@ public class AddOnOneToMany implements DataAccessAddOn {
@Override
public boolean isInsertAsync(final Field field) throws Exception {
// TODO: can be implemented later...
return false;
}
@Override
public boolean canRetrieve(final Field field) {
if (field.getType() != List.class) {
return false;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
@Override
public void generateQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) {
public void generateConcatQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options,
final Class<?> targetEntity,
final String mappedBy) throws Exception {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String remoteTableName = AnnotationTools.getTableName(targetEntity);
final String remoteTablePrimaryKeyName = AnnotationTools
.getFieldName(AnnotationTools.getPrimaryKeyField(targetEntity));
final String tmpRemoteVariable = "tmp_" + Integer.toString(count.value);
final String remoteDeletedFieldName = AnnotationTools.getDeletedFieldName(targetEntity);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteTablePrimaryKeyName);
querySelect.append(" ");
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querySelect.append("SEPARATOR ");
}
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
querySelect.append("') FROM ");
querySelect.append(remoteTableName);
querySelect.append(" ");
querySelect.append(tmpRemoteVariable);
querySelect.append(" WHERE ");
if (remoteDeletedFieldName != null) {
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteDeletedFieldName);
querySelect.append(" = false");
querySelect.append(" AND ");
}
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(mappedBy);
querySelect.append(" ");
querySelect.append(") AS ");
querySelect.append(name);
count.inc();
querySelect.append(" ");
}
@Override
public void fillFromQuery(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall)
throws SQLException, IllegalArgumentException, IllegalAccessException {
final Long foreignKey = rs.getLong(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy());
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
// Force a copy of the primaryKey to permit the async retrieve of the data
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" AS tmp_");
querySelect.append(Integer.toString(count.value));
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system
@Override
public void createTables(//
final String tableName, //
final Field primaryField, //
final Field field, //
final StringBuilder mainTableBuilder, //
final List<String> preActionList, //
final List<String> postActionList, //
final boolean createIfNotExist, //
final boolean createDrop, //
final int fieldId //
) throws Exception {
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, Long.class);
public void fillFromQuery(
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
if (objectClass == Long.class) {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = DataAccess.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} catch (final NumberFormatException ex) {
final List<UUID> idList = DataAccess.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
// TODO : refacto this table to manage a generic table with dynamic name to be serialize with the default system
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId) throws Exception {
// This is a remote field ==> nothing to generate (it is stored in the remote object
}
}

View File

@@ -1,132 +0,0 @@
package org.kar.archidata.dataAccess.addOn;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.addOn.SQLTableExternalForeinKeyAsList;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.validation.constraints.NotNull;
// TODO: maybe deprecated ==> use DataJson instead...
@Deprecated
public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR = "-";
/** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed)
* @return '-' string separated */
protected static String getStringOfIds(final List<Long> ids) {
final List<Long> tmp = new ArrayList<>(ids);
return tmp.stream().map(String::valueOf).collect(Collectors.joining(SEPARATOR));
}
@Override
public Class<?> getAnnotationClass() {
return SQLTableExternalForeinKeyAsList.class;
}
@Override
public String getSQLFieldType(final Field field) throws Exception {
final String fieldName = AnnotationTools.getFieldName(field);
try {
return DataFactory.convertTypeInSQL(String.class, fieldName);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public boolean isCompatibleField(final Field field) {
final SQLTableExternalForeinKeyAsList decorators = field.getDeclaredAnnotation(SQLTableExternalForeinKeyAsList.class);
return decorators != null;
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws SQLException, IllegalArgumentException, IllegalAccessException {
final Object data = field.get(rootObject);
iii.inc();
if (data == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
@SuppressWarnings("unchecked")
final String dataTmp = getStringOfIds((List<Long>) data);
ps.setString(iii.value, dataTmp);
}
}
@Override
public boolean canInsert(final Field field) {
return true;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return true;
}
@Override
public void generateQuery(//
@NotNull final String tableName, //
@NotNull final String primaryKey, //
@NotNull final Field field, //
@NotNull final StringBuilder querySelect, //
@NotNull final StringBuilder query, //
@NotNull final String name, //
@NotNull final CountInOut count, //
final QueryOptions options//
) {
count.inc();
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
}
@Override
public void fillFromQuery(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall)
throws SQLException, IllegalArgumentException, IllegalAccessException {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR);
field.set(data, idList);
count.inc();
}
@Override
public void createTables(//
final String tableName, //
final Field primaryField, //
final Field field, //
final StringBuilder mainTableBuilder, //
final List<String> preActionList, //
final List<String> postActionList, //
final boolean createIfNotExist, //
final boolean createDrop, //
final int fieldId //
) throws Exception {
// TODO Auto-generated method stub
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, String.class);
}
}

View File

@@ -1,11 +1,11 @@
package org.kar.archidata.dataAccess.addOn.model;
import org.kar.archidata.model.GenericData;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableLongLong extends GenericData {
public class LinkTableLongLong extends UUIDGenericDataSoftDelete {
public LinkTableLongLong() {
// nothing to do...
}

View File

@@ -2,12 +2,12 @@ package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.GenericData;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableLongUUID extends GenericData {
public class LinkTableLongUUID extends UUIDGenericDataSoftDelete {
public LinkTableLongUUID() {
// nothing to do...
}

View File

@@ -2,12 +2,12 @@ package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.GenericData;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableUUIDLong extends GenericData {
public class LinkTableUUIDLong extends UUIDGenericDataSoftDelete {
public LinkTableUUIDLong() {
// nothing to do...
}

View File

@@ -2,12 +2,12 @@ package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.GenericData;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableUUIDUUID extends GenericData {
public class LinkTableUUIDUUID extends UUIDGenericDataSoftDelete {
public LinkTableUUIDUUID() {
// nothing to do...
}

View File

@@ -3,12 +3,10 @@ package org.kar.archidata.dataAccess.addOn.model;
import java.util.List;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversLongLong extends GenericData {
public class TableCoversLongLong {
public TableCoversLongLong() {
// nothing to do...
}
@@ -18,11 +16,10 @@ public class TableCoversLongLong extends GenericData {
this.covers = covers;
}
@Column(nullable = false)
@Id
public Long id;
@DataJson()
@Column(nullable = false)
public List<Long> covers;
}

View File

@@ -4,12 +4,10 @@ import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversLongUUID extends GenericData {
public class TableCoversLongUUID {
public TableCoversLongUUID() {
// nothing to do...
}
@@ -19,11 +17,10 @@ public class TableCoversLongUUID extends GenericData {
this.covers = covers;
}
@Column(nullable = false)
@Id
public Long id;
@DataJson()
@Column(nullable = false)
public List<UUID> covers;
}

View File

@@ -4,26 +4,22 @@ import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversUUIDLong extends GenericData {
public class TableCoversUUIDLong {
public TableCoversUUIDLong() {
// nothing to do...
}
public TableCoversUUIDLong(final UUID id, final List<Long> covers) {
this.id = id;
public TableCoversUUIDLong(final UUID uuid, final List<Long> covers) {
this.uuid = uuid;
this.covers = covers;
}
@Column(nullable = false)
@Id
public UUID id;
public UUID uuid;
@DataJson()
@Column(nullable = false)
public List<Long> covers;
}

View File

@@ -4,26 +4,22 @@ import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversUUIDUUID extends GenericData {
public class TableCoversUUIDUUID {
public TableCoversUUIDUUID() {
// nothing to do...
}
public TableCoversUUIDUUID(final UUID id, final List<UUID> covers) {
this.id = id;
public TableCoversUUIDUUID(final UUID uuid, final List<UUID> covers) {
this.uuid = uuid;
this.covers = covers;
}
@Column(nullable = false)
@Id
public UUID id;
public UUID uuid;
@DataJson()
@Column(nullable = false)
public List<UUID> covers;
}

View File

@@ -0,0 +1,137 @@
Archi-data
==========
Archi-data is a framework that simplify:
- Creating a REST server with:
- Right control
- Swagger display interface
- Normalize error generate by the server
- Access to the DB:
- introspect Object and insert in the TD (SQLITE & MY-SQL)
- Manage migration
- JPA checker for many generic request
- simplify the request of the Test-service
Documentation:
--------------
The documentation is available [here](doc/index.md)
Develop in cmd-line:
--------------------
The first step is configuring your JAVA version (or select the JVM with the OS)
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
```
Install the dependency:
```bash
mvn install
```
Run the test
```bash
mvn test
```
Install it for external use
```bash
mvn install
```
Develop With Eclipse:
--------------------
Import the project:
- Open a (new) project on eclipse
- `File` -> `Import`
- `Maven` -> `Existing Maven project`
- Select the `pom.xml` file and click on import
Run the Test:
- Open the package `test.kar.archidata`
- Click right on it
- Select `Debug As` -> `JUnit Test`
Install in the local maven repository:
- Click right on the `pom.xml` file
- Select `Run As` -> `Maven install`
Some tools:
============
Auto-update dependency:
-----------------------
Auto-update to the last version dependency:
```bash
mvn versions:use-latest-versions
```
Format the code
---------------
Simply run the cmd-line:
```bash
mvn formatter:format
```
Reformat XML file like the pom.xml
```bash
XMLLINT_INDENT=" " xmllint --format "back/pom.xml" -o "back/pom.xml"
```
Enable the pre-commit checker
-----------------------------
```bash
./tools/configure_precommit.bash
```
> **_Note_**: You can change the code in `.git/hooks/pre-commit` by replacing `formatter:verify` with `formatter:format` to auto format the code @ every commit
Add Gitea in the dependency for the registry:
=============================================
Read instruction for tocken in ~/.m2/setting.xml
edit file: ```~/.m2/settings.xml```
```xml
<settings>
<servers>
<server>
<id>gitea</id>
<configuration>
<httpHeaders>
<property>
<name>Authorization</name>
<value>token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
</settings>
```
release:
========
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
mvn install
mvn deploy
```

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** This option permit to access on deleted items of a table */
public class AccessDeletedItems extends QueryOption {
public AccessDeletedItems() {}

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class CheckFunction extends QueryOption {
private final CheckFunctionInterface checker;

View File

@@ -3,6 +3,7 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.QueryOptions;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckFunctionInterface {
@@ -11,10 +12,11 @@ public interface CheckFunctionInterface {
* @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked.
* @throws Exception Exception is generate if the data are incorrect. */
void check(final String baseName, Object data, List<String> filterValue) throws Exception;
void check(final String baseName, Object data, List<String> filterValue, final QueryOptions options)
throws Exception;
default void checkAll(final String baseName, final Object data) throws Exception {
check(baseName, data, AnnotationTools.getAllFieldsNames(data.getClass()));
default void checkAll(final String baseName, final Object data, final QueryOptions options) throws Exception {
check(baseName, data, AnnotationTools.getAllFieldsNames(data.getClass()), options);
}
}

View File

@@ -2,10 +2,16 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.dataAccess.QueryOptions;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class CheckFunctionVoid implements CheckFunctionInterface {
@Override
public void check(final String baseName, Object data, List<String> filterValue) {
public void check(
final String baseName,
final Object data,
final List<String> filterValue,
final QueryOptions options) {
}

View File

@@ -16,6 +16,7 @@ import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.exception.InputException;
import org.slf4j.Logger;
@@ -37,7 +38,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
* @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked.
* @throws Exception Exception is generate if the data are incorrect. */
void check(final String baseName, final K data) throws Exception;
void check(final String baseName, final K data, final QueryOptions options) throws Exception;
}
protected Map<String, List<CheckInterface<T>>> checking = null;
@@ -66,19 +67,20 @@ public class CheckJPA<T> implements CheckFunctionInterface {
for (final Field field : this.clazz.getFields()) {
final String fieldName = field.getName(); // AnnotationTools.getFieldName(field);
if (AnnotationTools.isPrimaryKey(field)) {
add(fieldName, (final String baseName, final T data) -> {
throw new InputException(baseName + fieldName, "This is a '@Id' (primaryKey) ==> can not be change");
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
throw new InputException(baseName + fieldName,
"This is a '@Id' (primaryKey) ==> can not be change");
});
}
if (AnnotationTools.getConstraintsNotNull(field)) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
if (field.get(data) == null) {
throw new InputException(baseName + fieldName, "Can not be null");
}
});
}
if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
throw new InputException(baseName + fieldName, "It is forbidden to change this field");
});
}
@@ -87,7 +89,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
if (type == Long.class || type == long.class) {
final Long maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -100,7 +102,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
}
final Long minValue = AnnotationTools.getConstraintsMin(field);
if (minValue != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -113,14 +115,19 @@ public class CheckJPA<T> implements CheckFunctionInterface {
}
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
final Condition conditionCheck = condCheckers.isEmpty() ? null
: condCheckers.get(0).toCondition();
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem,
conditionCheck);
if (count == 0) {
throw new InputException(baseName + fieldName, "Foreign element does not exist in the DB:" + elem);
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
}
@@ -129,7 +136,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final int maxValue = maxValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -143,7 +150,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final int minValue = minValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -156,28 +163,30 @@ public class CheckJPA<T> implements CheckFunctionInterface {
}
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
if (count == 0) {
throw new InputException(baseName + fieldName, "Foreign element does not exist in the DB:" + elem);
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
}
} else if (type == UUID.class) {
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
if (count == 0) {
throw new InputException(baseName + fieldName, "Foreign element does not exist in the DB:" + elem);
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
}
@@ -187,7 +196,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final float maxValue = maxValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -201,7 +210,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final float minValue = minValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -216,7 +225,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final double maxValue = maxValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -230,7 +239,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final double minValue = minValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
@@ -250,44 +259,49 @@ public class CheckJPA<T> implements CheckFunctionInterface {
} else if (type == String.class) {
final int maxSizeString = AnnotationTools.getLimitSize(field);
if (maxSizeString > 0) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (elemTyped.length() > maxSizeString) {
throw new InputException(baseName + fieldName, "Too long size must be <= " + maxSizeString);
throw new InputException(baseName + fieldName,
"Too long size must be <= " + maxSizeString);
}
});
}
final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) {
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (elemTyped.length() > limitSize.max()) {
throw new InputException(baseName + fieldName, "Too long size (constraints) must be <= " + limitSize.max());
throw new InputException(baseName + fieldName,
"Too long size (constraints) must be <= " + limitSize.max());
}
if (elemTyped.length() < limitSize.min()) {
throw new InputException(baseName + fieldName, "Too small size (constraints) must be >= " + limitSize.max());
throw new InputException(baseName + fieldName,
"Too small size (constraints) must be >= " + limitSize.min());
}
});
}
final String patternString = AnnotationTools.getConstraintsPattern(field);
if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString);
add(fieldName, (final String baseName, final T data) -> {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (!pattern.matcher(elemTyped).find()) {
throw new InputException(baseName + fieldName, "does not match the required pattern (constraints) must be '" + patternString + "'");
throw new InputException(baseName + fieldName,
"does not match the required pattern (constraints) must be '" + patternString
+ "'");
}
});
}
@@ -295,9 +309,10 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final DataJson jsonAnnotation = AnnotationTools.getDataJson(field);
if (jsonAnnotation != null && jsonAnnotation.checker() != CheckFunctionVoid.class) {
// Here if we have an error it crash at start and no new instance after creation...
final CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor().newInstance();
add(fieldName, (final String baseName, final T data) -> {
instance.checkAll(baseName + fieldName + ".", field.get(data));
final CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor()
.newInstance();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
instance.checkAll(baseName + fieldName + ".", field.get(data), options);
});
}
} else if (type.isEnum()) {
@@ -306,8 +321,17 @@ public class CheckJPA<T> implements CheckFunctionInterface {
// keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) {
// Create the request ...
add(fieldName, (final String baseName, final T data) -> {
final Object other = DataAccess.getWhere(this.clazz, new Condition(new QueryCondition(fieldName, "==", field.get(data))));
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> {
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
Object other = null;
if (condCheckers.isEmpty()) {
other = DataAccess.getWhere(this.clazz,
new Condition(new QueryCondition(fieldName, "==", field.get(data))));
} else {
other = DataAccess.getWhere(this.clazz,
new Condition(new QueryCondition(fieldName, "==", field.get(data))),
condCheckers.get(0).toCondition());
}
if (other != null) {
throw new InputException(baseName + fieldName, "Name already exist in the DB");
}
@@ -322,7 +346,11 @@ public class CheckJPA<T> implements CheckFunctionInterface {
}
@Override
public void check(final String baseName, final Object data, final List<String> filterValue) throws Exception {
public void check(
final String baseName,
final Object data,
final List<String> filterValue,
final QueryOptions options) throws Exception {
if (this.checking == null) {
initialize();
}
@@ -337,13 +365,13 @@ public class CheckJPA<T> implements CheckFunctionInterface {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(baseName, dataCasted);
action.check(baseName, dataCasted, options);
}
}
checkTyped(dataCasted, filterValue);
checkTyped(dataCasted, filterValue, options);
}
public void checkTyped(final T data, final List<String> filterValue) throws Exception {
public void checkTyped(final T data, final List<String> filterValue, final QueryOptions options) throws Exception {
// nothing to do ...
}
}

View File

@@ -4,7 +4,6 @@ import java.sql.PreparedStatement;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.QueryItem;
import org.kar.archidata.dataAccess.QueryOption;
import org.kar.archidata.dataAccess.QueryOptions;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
@@ -31,7 +30,11 @@ public class Condition extends QueryOption {
}
}
public void whereAppendQuery(final StringBuilder query, final String tableName, final QueryOptions options, final String deletedFieldName) {
public void whereAppendQuery(
final StringBuilder query,
final String tableName,
final QueryOptions options,
final String deletedFieldName) {
boolean exclude_deleted = true;
if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class);

View File

@@ -0,0 +1,25 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryItem;
/** Condition model apply to the check models. */
public class ConditionChecker extends QueryOption {
public final QueryItem condition;
public ConditionChecker(final QueryItem items) {
this.condition = items;
}
public ConditionChecker(final Condition cond) {
this.condition = cond.condition;
}
public ConditionChecker() {
this.condition = null;
}
public Condition toCondition() {
return new Condition(this.condition);
}
}

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** This option permit to create the DROP `Table` IF EXIST in the generation of structure. */
public class CreateDropTable extends QueryOption {
public CreateDropTable() {}

View File

@@ -1,9 +1,9 @@
package org.kar.archidata.dataAccess.options;
import java.io.IOException;
import java.util.List;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.QueryOption;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.db.DBEntry;
@@ -18,15 +18,15 @@ public class DBInterfaceOption extends QueryOption {
this.root = false;
}
public DBInterfaceOption(final DBConfig config, boolean root) {
public DBInterfaceOption(final DBConfig config, final boolean root) {
this.config = config;
this.root = root;
}
public DBEntry getEntry(QueryOptions options) throws IOException {
public DBEntry getEntry(final QueryOptions options) throws IOException {
if (this.entry == null) {
final DBInterfaceRoot isRoot = options.get(DBInterfaceRoot.class);
this.entry = DBEntry.createInterface(this.config, isRoot != null && isRoot.getRoot());
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);
this.entry = DBEntry.createInterface(this.config, isRoot.size() == 1 && isRoot.get(0).getRoot());
}
return this.entry;
}
@@ -35,13 +35,16 @@ public class DBInterfaceOption extends QueryOption {
return this.root;
}
public static DBEntry getAutoEntry(QueryOptions options) throws IOException {
final DBInterfaceOption dbOption = options.get(DBInterfaceOption.class);
if (dbOption == null) {
final DBInterfaceRoot isRoot = options.get(DBInterfaceRoot.class);
return DBEntry.createInterface(GlobalConfiguration.dbConfig, isRoot != null && isRoot.getRoot());
public static DBEntry getAutoEntry(final QueryOptions options) throws IOException {
if (options == null) {
return DBEntry.createInterface(GlobalConfiguration.dbConfig, false);
}
final List<DBInterfaceOption> dbOption = options.get(DBInterfaceOption.class);
if (dbOption.size() == 0) {
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);
return DBEntry.createInterface(GlobalConfiguration.dbConfig, isRoot.size() == 1 && isRoot.get(0).getRoot());
} else {
return dbOption.getEntry(options);
return dbOption.get(0).getEntry(options);
}
}

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
public class DBInterfaceRoot extends QueryOption {
private final boolean root;

View File

@@ -2,8 +2,6 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.dataAccess.QueryOption;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class FilterValue extends QueryOption {
public final List<String> filterValue;

View File

@@ -1,8 +1,10 @@
package org.kar.archidata.dataAccess;
package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement;
import java.util.List;
import org.kar.archidata.dataAccess.CountInOut;
public class GroupBy extends QueryOption {
protected final List<String> childs;

View File

@@ -1,7 +1,10 @@
package org.kar.archidata.dataAccess;
package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
public class Limit extends QueryOption {
protected final long limit;

View File

@@ -1,8 +1,10 @@
package org.kar.archidata.dataAccess;
package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement;
import java.util.List;
import org.kar.archidata.dataAccess.CountInOut;
public class OrderBy extends QueryOption {
protected final List<OrderItem> childs;

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.dataAccess;
package org.kar.archidata.dataAccess.options;
public class OrderItem {
public enum Order {

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** Option that permit to access to a table structure with an other name that is define in the structure. Note: Internal use for link tables (see:
* org.kar.archidata.dataAccess.addOn.model.LinkTable). */
public class OverrideTableName extends QueryOption {

View File

@@ -0,0 +1,3 @@
package org.kar.archidata.dataAccess.options;
public class QueryOption {}

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class ReadAllColumn extends QueryOption {
public ReadAllColumn() {}

View File

@@ -1,7 +1,5 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** Internal option that permit to transmit the Key when updating the ManyToMany values (first step). */
public class TransmitKey extends QueryOption {
private final Object key;

View File

@@ -14,7 +14,8 @@ public class DBConfig {
private final String dbName;
private final boolean keepConnected;
public DBConfig(final String type, final String hostname, final Integer port, final String login, final String password, final String dbName, final boolean keepConnected) {
public DBConfig(final String type, final String hostname, final Integer port, final String login,
final String password, final String dbName, final boolean keepConnected) {
if (type == null) {
this.type = "mysql";
} else {
@@ -38,8 +39,9 @@ public class DBConfig {
@Override
public String toString() {
return "DBConfig{type='" + this.type + '\'' + ", hostname='" + this.hostname + '\'' + ", port=" + this.port + ", login='" + this.login + '\'' + ", password='" + this.password + '\''
+ ", dbName='" + this.dbName + "' }";
return "DBConfig{type='" + this.type + '\'' + ", hostname='" + this.hostname + '\'' + ", port=" + this.port
+ ", login='" + this.login + '\'' + ", password='" + this.password + '\'' + ", dbName='" + this.dbName
+ "' }";
}
public String getHostname() {
@@ -81,8 +83,10 @@ public class DBConfig {
return "jdbc:sqlite:" + this.hostname + ".db";
}
if (isRoot) {
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port
+ "/?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
}
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/" + this.dbName + "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
return "jdbc:" + this.type + "://" + this.hostname + ":" + this.port + "/" + this.dbName
+ "?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC";
}
}

View File

@@ -50,7 +50,8 @@ public class DBEntry implements Closeable {
public void connectRoot() throws IOException {
try {
this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(), this.config.getPassword());
this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(),
this.config.getPassword());
} catch (final SQLException ex) {
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}
@@ -59,7 +60,8 @@ public class DBEntry implements Closeable {
public void connect() throws IOException {
try {
this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(), this.config.getPassword());
this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(),
this.config.getPassword());
} catch (final SQLException ex) {
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}

View File

@@ -1,19 +1,34 @@
package org.kar.archidata.exception;
import org.kar.archidata.api.DataResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response;
public class FailException extends Exception {
private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class);
private static final long serialVersionUID = 1L;
public final Response.Status status;
public final Exception exception;
public FailException(final Response.Status status, final String message) {
super(message);
this.status = status;
this.exception = null;
}
public FailException(final Response.Status status, final String message, final Exception ex) {
super(message);
this.status = status;
this.exception = ex;
ex.printStackTrace();
LOGGER.error("Generate Fail exception with exceptionData: {}", ex.getMessage());
}
public FailException(final String message) {
super(message);
this.status = Response.Status.BAD_REQUEST;
this.exception = null;
}
}

View File

@@ -19,7 +19,8 @@ public class RESTErrorResponseExeption extends Exception {
this.statusMessage = null;
}
public RESTErrorResponseExeption(final UUID uuid, final String time, final String name, final String message, final int status, final String statusMessage) {
public RESTErrorResponseExeption(final UUID uuid, final String time, final String name, final String message,
final int status, final String statusMessage) {
this.uuid = uuid;
this.time = time;
this.name = name;
@@ -30,8 +31,9 @@ public class RESTErrorResponseExeption extends Exception {
@Override
public String toString() {
return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", name=" + this.name + ", message=" + this.message + ", status=" + this.status + ", statusMessage="
+ this.statusMessage + "]";
return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", name=" + this.name
+ ", message=" + this.message + ", status=" + this.status + ", statusMessage=" + this.statusMessage
+ "]";
}
}

View File

@@ -0,0 +1,74 @@
package org.kar.archidata.externalRestApi;
import java.util.ArrayList;
import java.util.List;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ModelGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AnalyzeApi {
static final Logger LOGGER = LoggerFactory.getLogger(AnalyzeApi.class);
protected final List<ApiGroupModel> apiModels = new ArrayList<>();
protected final ModelGroup modelGroup = new ModelGroup();
public void addAllModel(final List<Class<?>> classes) throws Exception {
this.modelGroup.addAll(classes);
analyzeModels();
}
public void addModel(final Class<?> clazz) throws Exception {
this.modelGroup.add(clazz);
analyzeModels();
}
public void addApi(final Class<?> clazz) throws Exception {
this.apiModels.add(new ApiGroupModel(clazz, this.modelGroup));
analyzeModels();
}
public void addAllApi(final List<Class<?>> classes) throws Exception {
for (final Class<?> clazz : classes) {
this.apiModels.add(new ApiGroupModel(clazz, this.modelGroup));
}
analyzeModels();
}
public List<ApiGroupModel> getAllApi() {
return this.apiModels;
}
public List<ClassModel> getAllModel() {
return this.modelGroup.getModels();
}
private void analyzeModels() throws Exception {
final List<ClassModel> dones = new ArrayList<>();
while (dones.size() < getAllModel().size()) {
final List<ClassModel> copyList = new ArrayList<>(this.modelGroup.getModels());
for (final ClassModel model : copyList) {
if (dones.contains(model)) {
continue;
}
LOGGER.info("Analyze: {}", model);
model.analyze(this.modelGroup);
dones.add(model);
}
}
}
public List<ClassModel> getCompatibleModels(final List<Class<?>> search) {
final List<ClassModel> out = new ArrayList<>();
for (final ClassModel model : getAllModel()) {
if (search.contains(model.getOriginClasses())) {
out.add(model);
}
}
if (out.isEmpty()) {
return null;
}
return out;
}
}

View File

@@ -0,0 +1,232 @@
package org.kar.archidata.externalRestApi;
import java.io.FileWriter;
import java.io.InputStream;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.externalRestApi.dot.DotApiGeneration;
import org.kar.archidata.externalRestApi.dot.DotClassElement;
import org.kar.archidata.externalRestApi.dot.DotClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.dot.DotClassElementGroup;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class DotGenerateApi {
public static void generateApi(final AnalyzeApi api, final String pathDotFile) throws Exception {
final List<DotClassElement> localModel = generateApiModel(api);
final DotClassElementGroup dotGroup = new DotClassElementGroup(localModel);
try (final FileWriter myWriter = new FileWriter(pathDotFile)) {
myWriter.write("""
# Architecture auto-generated file
digraph UML_Class_diagram {
#rankdir=NS;
graph [
pad="0.5"
nodesep="1"
#ranksep="2"
label="Rest API server Model"
labelloc="t"
fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"
]
node [
fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"
shape=record
style=filled
fillcolor=gray95
]
edge [fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"]
""");
/*
myWriter.write("""
subgraph REST_API {
style=filled;
color=lightgrey;
label="REST API";
rankdir=LR;
""");
*/
for (final ApiGroupModel element : api.apiModels) {
final String tmp = DotApiGeneration.generateApiFile(element, dotGroup);
myWriter.write(tmp);
myWriter.write("\n");
}
// create an invisible link to force all element to be link together:
if (false) {
String previous = null;
for (final ApiGroupModel element : api.apiModels) {
if (previous == null) {
previous = element.name;
continue;
}
myWriter.write("\t{ ");
myWriter.write(previous);
myWriter.write(":s -> ");
previous = element.name;
myWriter.write(previous);
myWriter.write(":n [style=invis]}\n");
}
}
/*
myWriter.write("""
}
""");
myWriter.write("""
subgraph Models {
style=filled;
color=lightgrey;
label="Models";
rankdir=NS;
""");
*/
// Generates all MODEL files
for (final DotClassElement element : localModel) {
final String tmp = element.generateFile(dotGroup);
myWriter.write(tmp);
myWriter.write("\n");
}
/*
myWriter.write("""
}
""");
*/
myWriter.write("""
}
""");
}
}
private static List<DotClassElement> generateApiModel(final AnalyzeApi api) throws Exception {
// First step is to add all specific basic elements the wrap correctly the model
final List<DotClassElement> dotModels = new ArrayList<>();
List<ClassModel> models = api.getCompatibleModels(List.of(Void.class, void.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "void", "void", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Object.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Object", "object", null, "Object", DefinedPosition.NATIVE));
}
// Map is binded to any ==> can not determine this complex model for now
models = api.getCompatibleModels(List.of(Map.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Object", "any", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(String.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "String", "string", null, "String", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(
List.of(InputStream.class, FormDataContentDisposition.class, ContentDisposition.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "File", "File", null, "File", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Boolean.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Boolean", "boolean", null, "Boolean", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(boolean.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "boolean", "boolean", null, "boolean", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(UUID.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "UUID", "UUID", "isUUID", "UUID", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(long.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "long", "Long", "isLong", "long", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Long.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Long", "Long", "isLong", "Long", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(short.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "short", "Short", "isShort", "short", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Short.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Short", "Short", "isShort", "Short", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(int.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "int", "Integer", "isInteger", "int", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Integer.class));
if (models != null) {
dotModels.add(
new DotClassElement(models, "Integer", "Integer", "isInteger", "Integer", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(double.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Double", "Double", "isDouble", "double", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Double.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Double", "Double", "isDouble", "Double", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(float.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "float", "Float", "isFloat", "float", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Float.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Float", "Float", "isFloat", "Float", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Instant.class));
if (models != null) {
dotModels.add(
new DotClassElement(models, "Instant", "Instant", "isInstant", "Instant", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Date.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Date", "IsoDate", "isIsoDate", "Date", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Timestamp.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Timestamp", "Timestamp", "isTimestamp", "Timestamp",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalDate.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "LocalDate", "LocalDate", "isLocalDate", "LocalDate",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalTime.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "LocalTime", "LocalTime", "isLocalTime", "LocalTime",
DefinedPosition.BASIC));
}
// needed for Rest interface
api.addModel(RestErrorResponse.class);
for (final ClassModel model : api.getAllModel()) {
boolean alreadyExist = false;
for (final DotClassElement elem : dotModels) {
if (elem.isCompatible(model)) {
alreadyExist = true;
break;
}
}
if (alreadyExist) {
continue;
}
dotModels.add(new DotClassElement(model));
}
return dotModels;
}
}

View File

@@ -0,0 +1,8 @@
package org.kar.archidata.externalRestApi;
public class PythonGenerateApi {
public static void generateApi(final AnalyzeApi api) {
}
}

View File

@@ -0,0 +1,242 @@
package org.kar.archidata.externalRestApi;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.typescript.TsApiGeneration;
import org.kar.archidata.externalRestApi.typescript.TsClassElement;
import org.kar.archidata.externalRestApi.typescript.TsClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.typescript.TsClassElementGroup;
public class TsGenerateApi {
/**
* Generate a full API tree for Typescript in a specific folder.
* This generate a folder containing a full API with "model" filder and "api" folder.
* The generation depend of Zod and can be strict compile.
* @param api Data model to generate the api
* @param pathPackage Path to store the api.
*/
public static void generateApi(final AnalyzeApi api, final String pathPackage) throws Exception {
final List<TsClassElement> localModel = generateApiModel(api);
final TsClassElementGroup tsGroup = new TsClassElementGroup(localModel);
// Generates all MODEL files
for (final TsClassElement element : localModel) {
element.generateFile(pathPackage, tsGroup);
}
// Generate index of model files
createModelIndex(pathPackage, tsGroup);
for (final ApiGroupModel element : api.apiModels) {
TsApiGeneration.generateApiFile(element, pathPackage, tsGroup);
}
// Generate index of model files
createResourceIndex(pathPackage, api.apiModels);
createIndex(pathPackage);
copyResourceFile("rest-tools.ts", pathPackage + File.separator + "rest-tools.ts");
}
private static void createIndex(final String pathPackage) throws IOException {
final String out = """
/**
* Interface of the server (auto-generated code)
*/
export * from \"./model\";
export * from \"./api\";
export * from \"./rest-tools\";
""";
final FileWriter myWriter = new FileWriter(pathPackage + File.separator + "index.ts");
myWriter.write(out);
myWriter.close();
}
private static void createResourceIndex(final String pathPackage, final List<ApiGroupModel> apiModels)
throws IOException {
final StringBuilder out = new StringBuilder("""
/**
* Interface of the server (auto-generated code)
*/
""");
final List<String> files = new ArrayList<>();
for (final ApiGroupModel elem : apiModels) {
files.add(TsClassElement.determineFileName(elem.name));
}
Collections.sort(files);
for (final String elem : files) {
out.append("export * from \"./");
out.append(elem);
out.append("\"\n");
}
final FileWriter myWriter = new FileWriter(pathPackage + File.separator + "api" + File.separator + "index.ts");
myWriter.write(out.toString());
myWriter.close();
}
private static void createModelIndex(final String pathPackage, final TsClassElementGroup tsGroup)
throws IOException {
final StringBuilder out = new StringBuilder("""
/**
* Interface of the server (auto-generated code)
*/
""");
final List<String> files = new ArrayList<>();
for (final TsClassElement elem : tsGroup.getTsElements()) {
if (elem.nativeType == DefinedPosition.NATIVE) {
continue;
}
files.add(elem.fileName);
}
Collections.sort(files);
for (final String elem : files) {
out.append("export * from \"./");
out.append(elem);
out.append("\"\n");
}
final FileWriter myWriter = new FileWriter(
pathPackage + File.separator + "model" + File.separator + "index.ts");
myWriter.write(out.toString());
myWriter.close();
}
private static List<TsClassElement> generateApiModel(final AnalyzeApi api) throws Exception {
// First step is to add all specific basic elements the wrap correctly the model
final List<TsClassElement> tsModels = new ArrayList<>();
List<ClassModel> models = api.getCompatibleModels(List.of(Void.class, void.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "void", "void", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Object.class));
if (models != null) {
tsModels.add(
new TsClassElement(models, "zod.any()", "object", null, "zod.object()", DefinedPosition.NATIVE));
}
// Map is binded to any ==> can not determine this complex model for now
models = api.getCompatibleModels(List.of(Map.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "zod.any()", "any", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(String.class));
if (models != null) {
tsModels.add(
new TsClassElement(models, "zod.string()", "string", null, "zod.string()", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(
List.of(InputStream.class, FormDataContentDisposition.class, ContentDisposition.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "z.instanceof(File)", "File", null, "z.instanceof(File)",
DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Boolean.class, boolean.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "zod.boolean()", "boolean", null, "zod.boolean()",
DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(UUID.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodUUID", "UUID", "isUUID", "zod.string().uuid()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Long.class, long.class));
if (models != null) {
tsModels.add(
new TsClassElement(models, "ZodLong", "Long", "isLong", "zod.number()", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Short.class, short.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodShort", "Short", "isShort", "zod.number().safe()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Integer.class, int.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodInteger", "Integer", "isInteger", "zod.number().safe()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Double.class, double.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodDouble", "Double", "isDouble", "zod.number()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Float.class, float.class));
if (models != null) {
tsModels.add(
new TsClassElement(models, "ZodFloat", "Float", "isFloat", "zod.number()", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Instant.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodInstant", "Instant", "isInstant", "zod.string()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Date.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodIsoDate", "IsoDate", "isIsoDate",
"zod.string().datetime({ precision: 3 })", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Timestamp.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodTimestamp", "Timestamp", "isTimestamp",
"zod.string().datetime({ precision: 3 })", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalDate.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodLocalDate", "LocalDate", "isLocalDate", "zod.string().date()",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalTime.class));
if (models != null) {
tsModels.add(new TsClassElement(models, "ZodLocalTime", "LocalTime", "isLocalTime", "zod.string().time()",
DefinedPosition.BASIC));
}
// needed for Rest interface
api.addModel(RestErrorResponse.class);
for (final ClassModel model : api.getAllModel()) {
boolean alreadyExist = false;
for (final TsClassElement elem : tsModels) {
if (elem.isCompatible(model)) {
alreadyExist = true;
break;
}
}
if (alreadyExist) {
continue;
}
tsModels.add(new TsClassElement(model));
}
return tsModels;
}
public static void copyResourceFile(final String name, final String destinationPath) throws IOException {
final InputStream ioStream = TsGenerateApi.class.getClassLoader().getResourceAsStream(name);
if (ioStream == null) {
throw new IllegalArgumentException("rest-tools.ts is not found");
}
final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream));
final FileWriter myWriter = new FileWriter(destinationPath);
String line;
while ((line = buffer.readLine()) != null) {
myWriter.write(line);
myWriter.write("\n");
}
ioStream.close();
myWriter.close();
}
}

View File

@@ -0,0 +1,414 @@
package org.kar.archidata.externalRestApi.dot;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.kar.archidata.externalRestApi.dot.DotClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ApiModel;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DotApiGeneration {
static final Logger LOGGER = LoggerFactory.getLogger(DotApiGeneration.class);
public static String generateClassEnumModelTypescript(
final ClassEnumModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
imports.add(model);
final DotClassElement dotModel = dotGroup.find(model);
return dotModel.dotTypeName;
}
public static String generateClassObjectModelTypescript(
final ClassObjectModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType != DefinedPosition.NATIVE) {
imports.add(model);
}
if (dotModel.nativeType != DefinedPosition.NORMAL) {
return dotModel.dotTypeName;
}
return dotModel.dotTypeName;
}
public static String generateClassMapModelTypescript(
final ClassMapModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("Map&lt;");
out.append(generateClassModelTypescript(model.keyModel, dotGroup, imports));
out.append(", ");
out.append(generateClassModelTypescript(model.valueModel, dotGroup, imports));
out.append("&gt;");
return out.toString();
}
public static String generateClassListModelTypescript(
final ClassListModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("List&lt;");
out.append(generateClassModelTypescript(model.valueModel, dotGroup, imports));
out.append("&gt;");
return out.toString();
}
public static String generateClassModelTypescript(
final ClassModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, dotGroup, imports);
}
if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, dotGroup, imports);
}
if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, dotGroup, imports);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, dotGroup, imports);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 0) {
return "void";
}
final StringBuilder out = new StringBuilder();
if (models.size() > 1) {
out.append("Union&lt;");
}
boolean isFirst = true;
for (final ClassModel model : models) {
if (isFirst) {
isFirst = false;
} else {
out.append(", ");
}
final String data = DotClassElement.generateClassModelTypescript(model, dotGroup);
out.append(data);
}
if (models.size() > 1) {
out.append("&gt;");
}
return out.toString();
}
public static List<String> generateClassModelsLinks(
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 0) {
return null;
}
final List<String> out = new ArrayList<>();
final boolean isFirst = true;
for (final ClassModel model : models) {
final String data = DotClassElement.generateClassModelTypescriptLink(model, dotGroup);
if (data != null) {
out.add(data);
}
}
return out;
}
public static String capitalizeFirstLetter(final String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
public static String generateApiFile(final ApiGroupModel element, final DotClassElementGroup dotGroup)
throws IOException {
final StringBuilder data = new StringBuilder();
final StringBuilder outLinks = new StringBuilder();
data.append("""
%s [
shape=plain
label=<<table color="#FF3333" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td><b>%s</b><br/>(REST)</td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(element.name, element.name));
final Set<ClassModel> imports = new HashSet<>();
final Set<ClassModel> zodImports = new HashSet<>();
final Set<ClassModel> isImports = new HashSet<>();
final Set<ClassModel> writeImports = new HashSet<>();
final Set<String> toolImports = new HashSet<>();
for (final ApiModel interfaceElement : element.interfaces) {
final List<String> consumes = interfaceElement.consumes;
final List<String> produces = interfaceElement.produces;
final boolean needGenerateProgress = interfaceElement.needGenerateProgress;
/*
if (returnComplexModel != null) {
data.append(returnComplexModel.replaceAll("(?m)^", "\t"));
for (final ClassModel elem : interfaceElement.returnTypes) {
zodImports.addAll(elem.getDependencyGroupModels());
}
}
*/
data.append("\t\t\t\t\t<tr><td align=\"left\" port=\"");
data.append(interfaceElement.name);
data.append("\"><b> + ");
data.append(interfaceElement.name);
data.append("(");
boolean hasParam = false;
/*
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\tqueries: {");
for (final Entry<String, List<ClassModel>> queryEntry : interfaceElement.queries.entrySet()) {
data.append("\n\t\t\t");
data.append(queryEntry.getKey());
data.append("?: ");
data.append(generateClassModelsTypescript(queryEntry.getValue(), dotGroup, imports, false));
data.append(",");
}
data.append("\n\t\t},");
}
*/
/* fonctionnel mais trop de donnée
if (!interfaceElement.parameters.isEmpty()) {
//data.append("params: {");
for (final Entry<String, List<ClassModel>> paramEntry : interfaceElement.parameters.entrySet()) {
data.append("");
data.append(paramEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(paramEntry.getValue(), dotGroup, imports, false));
data.append(",");
}
//data.append("},");
}
*/
if (interfaceElement.unnamedElement.size() == 1) {
if (hasParam) {
data.append(", ");
}
hasParam = true;
data.append("data: ");
data.append(
generateClassModelTypescript(interfaceElement.unnamedElement.get(0), dotGroup, writeImports));
} else if (interfaceElement.multiPartParameters.size() != 0) {
if (hasParam) {
data.append(", ");
}
hasParam = true;
boolean hasParam2 = false;
data.append("data: {");
for (final Entry<String, List<ClassModel>> pathEntry : interfaceElement.multiPartParameters
.entrySet()) {
if (hasParam2) {
data.append(", ");
}
hasParam2 = true;
data.append(pathEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue(), dotGroup));
}
data.append("}");
}
data.append("): ");
/*
String tmp = DotClassElement.generateLocalModel(
final String ModelName,
final List<ClassModel> models,
final DotClassElementGroup dotGroup)
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
*/
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, dotGroup);
data.append(returnType);
final List<String> returnLinks = generateClassModelsLinks(interfaceElement.returnTypes, dotGroup);
for (final String link : returnLinks) {
outLinks.append("\t");
outLinks.append(element.name);
outLinks.append(":");
outLinks.append(interfaceElement.name);
outLinks.append(":e -> ");
outLinks.append(link);
outLinks.append(":NAME:w\n");
}
data.append("</b>");
//data.append("<br align=\"left\"/>&nbsp;&nbsp;&nbsp;&nbsp;");
data.append("</td></tr>\n\t\t\t\t\t\t\t<tr><td align=\"left\"> ");
/*
data.append("\n\t\t\trestModel: {");
data.append("\n\t\t\t\tendPoint: \"");
*/
data.append(interfaceElement.restEndPoint);
/*
data.append("\",");
data.append("\n\t\t\t\trequestType: HTTPRequestModel.");
toolImports.add("HTTPRequestModel");
data.append(interfaceElement.restTypeRequest);
data.append(",");
if (consumes != null) {
for (final String elem : consumes) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.MULTIPART,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.TEXT_PLAIN.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
break;
}
}
} else if (RestTypeRequest.DELETE.equals(interfaceElement.restTypeRequest)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
}
if (produces != null) {
if (produces.size() > 1) {
data.append("\n\t\t\t\taccept: produce,");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, dotGroup,
imports, false);
if (!"void".equals(returnType)) {
for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\taccept: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
}
}
}
}
}
data.append("\n\t\t\t},");
data.append("\n\t\t\trestConfig,");
if (!interfaceElement.parameters.isEmpty()) {
data.append("\n\t\t\tparams,");
}
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\t\tqueries,");
}
if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\t\tdata,");
} else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
data.append("\n\t\t\tcallback,");
}
data.append("\n\t\t}");
if (returnComplexModel != null) {
data.append(", is");
data.append(returnModelNameIfComplex);
} else {
final DotClassElement retType = dotGroup.find(interfaceElement.returnTypes.get(0));
if (retType.dotCheckType != null) {
data.append(", ");
data.append(retType.dotCheckType);
imports.add(interfaceElement.returnTypes.get(0));
}
}
*/
data.append("</td></tr>\n");
}
/*
data.append("\n}\n");
final StringBuilder out = new StringBuilder();
final List<String> toolImportsList = new ArrayList<>(toolImports);
Collections.sort(toolImportsList);
if (toolImportsList.size() != 0) {
out.append("import {");
for (final String elem : toolImportsList) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../rest-tools\";\n\n");
}
if (zodImports.size() != 0) {
out.append("import { z as zod } from \"zod\"\n");
}
final Set<String> finalImportSet = new TreeSet<>();
for (final ClassModel model : imports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(dotModel.dotTypeName);
}
for (final ClassModel model : isImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
if (dotModel.dotCheckType != null) {
finalImportSet.add(dotModel.dotCheckType);
}
}
for (final ClassModel model : zodImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add("Zod" + dotModel.dotTypeName);
}
for (final ClassModel model : writeImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(dotModel.dotTypeName + "Write");
}
if (finalImportSet.size() != 0) {
out.append("import {");
for (final String elem : finalImportSet) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../model\";\n\n");
}
out.append(data.toString());
*/
data.append("""
</table>
</td>
</tr>
</table>>
]
""");
data.append(outLinks.toString());
return data.toString();
}
}

View File

@@ -0,0 +1,485 @@
package org.kar.archidata.externalRestApi.dot;
import java.io.IOException;
import java.util.List;
import java.util.Map.Entry;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel.FieldProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DotClassElement {
static final Logger LOGGER = LoggerFactory.getLogger(DotClassElement.class);
public enum DefinedPosition {
NATIVE, // Native element of dot language.
BASIC, // basic wrapping for JAVA type.
NORMAL // Normal Object to interpret.
}
public List<ClassModel> models;
public String zodName;
public String dotTypeName;
public String dotCheckType;
public String declaration;
public String fileName = null;
public String comment = null;
public DefinedPosition nativeType = DefinedPosition.NORMAL;
public static String determineFileName(final String className) {
return className.replaceAll("([a-z])([A-Z])", "$1-$2").replaceAll("([A-Z])([A-Z][a-z])", "$1-$2").toLowerCase();
}
public DotClassElement(final List<ClassModel> model, final String zodName, final String dotTypeName,
final String dotCheckType, final String declaration, final DefinedPosition nativeType) {
this.models = model;
this.zodName = zodName;
this.dotTypeName = dotTypeName;
this.declaration = declaration;
this.nativeType = nativeType;
}
public DotClassElement(final ClassModel model) {
this.models = List.of(model);
this.dotTypeName = model.getOriginClasses().getSimpleName();
this.declaration = null;
}
public boolean isCompatible(final ClassModel model) {
return this.models.contains(model);
}
public String generateEnum(final ClassEnumModel model, final DotClassElementGroup dotGroup) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("""
%s [
shape=plain
label=<<table color="#33FF33" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td port="NAME"><b>%s</b><br/>(ENUM)</td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(this.dotTypeName, this.dotTypeName));
final boolean first = true;
for (final Entry<String, Object> elem : model.getListOfValues().entrySet()) {
out.append("\t\t\t\t\t\t<tr><td align=\"left\"><b> + ");
out.append(elem.getKey());
out.append("</b> = ");
if (elem.getValue() instanceof final Integer value) {
out.append(value);
} else {
out.append("'");
out.append(elem.getValue());
out.append("'");
}
out.append("</td></tr>\n");
}
out.append("""
</table>
</td>
</tr>
</table>>
]
""");
return out.toString();
}
public String generateImporDot(final List<ClassModel> depModels, final DotClassElementGroup dotGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
for (final ClassModel depModel : depModels) {
final DotClassElement dotModel = dotGroup.find(depModel);
if (dotModel.nativeType != DefinedPosition.NATIVE) {
out.append("import {");
out.append(dotModel.zodName);
out.append("} from \"./");
out.append(dotModel.fileName);
out.append("\";\n");
}
}
return out.toString();
}
private Object generateComment(final ClassObjectModel model) {
final StringBuilder out = new StringBuilder();
if (model.getDescription() != null || model.getExample() != null) {
out.append("/**\n");
if (model.getDescription() != null) {
for (final String elem : model.getDescription().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
}
if (model.getExample() != null) {
out.append(" * Example:\n");
out.append(" * ```\n");
for (final String elem : model.getExample().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
out.append(" * ```\n");
}
out.append(" */\n");
}
return out.toString();
}
public String optionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (field.nullable()) {
return ".optional()";
}
if (field.notNull()) {
return "";
}
// Other object:
if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) {
return "";
}
if (field.columnNotNull()) {
return "";
}
return ".optional()";
}
public String maxSizeZod(final FieldProperty field) {
final StringBuilder builder = new StringBuilder();
final Class<?> clazz = field.model().getOriginClasses();
if (clazz == String.class) {
if (field.sizeMin() > 0) {
builder.append(".min(");
builder.append(field.sizeMin());
builder.append(")");
}
if (field.sizeMax() > 0) {
builder.append(".max(");
builder.append(field.sizeMax());
builder.append(")");
}
}
if (clazz == short.class || clazz == Short.class || clazz == int.class || clazz == Integer.class
|| clazz == long.class || clazz == Long.class || clazz == float.class || clazz == Float.class
|| clazz == double.class || clazz == Double.class) {
if (field.min() != null && field.min() > 0) {
builder.append(".min(");
builder.append(field.min());
builder.append(")");
}
if (field.max() != null && field.max() > 0) {
builder.append(".max(");
builder.append(field.max());
builder.append(")");
}
}
return builder.toString();
}
public String readOnlyZod(final FieldProperty field) {
if (field.readOnly()) {
return ".readonly()";
}
return "";
}
public String generateBaseObject() {
final StringBuilder out = new StringBuilder();
return out.toString();
}
public String convertHtml(final String data) {
return data.replace("<", "&lt;").replace(">", "&gt;");
}
public static String generateClassModelTypescript(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof ClassEnumModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof ClassObjectModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof final ClassListModel fieldListModel) {
return generateDotList(fieldListModel, dotGroup);
} else if (model instanceof final ClassMapModel fieldMapModel) {
return generateDotMap(fieldMapModel, dotGroup);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelTypescriptLink(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof ClassEnumModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof ClassObjectModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
if (dotFieldModel.nativeType == DefinedPosition.NORMAL) {
return dotFieldModel.dotTypeName;
}
} else if (model instanceof final ClassListModel fieldListModel) {
final String className = generateDotListClassName(fieldListModel, dotGroup);
if (className != null) {
return className;
}
} else if (model instanceof final ClassMapModel fieldMapModel) {
final String className = generateDotMapClassName(fieldMapModel, dotGroup);
if (className != null) {
return className;
}
}
return null;
}
public String generateObject(final ClassObjectModel model, final DotClassElementGroup dotGroup) throws IOException {
final StringBuilder out = new StringBuilder();
final StringBuilder outLinks = new StringBuilder();
out.append("""
%s [
shape=plain
ranksep="2"
label=<<table color="#000000" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td port="NAME"><b>%s</b></td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(this.dotTypeName, this.dotTypeName));
String inheritence = null;
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final DotClassElement dotParentModel = dotGroup.find(parentClass);
inheritence = dotParentModel.dotTypeName;
}
if (model.getFields().size() == 0) {
out.append("\t\t\t\t\t\t<tr><td> <i>(empty)</i> </td></tr>");
}
for (final FieldProperty field : model.getFields()) {
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t\t\t\t\t\t<tr><td align=\"left\"><i> // ");
out.append(convertHtml(field.comment()));
out.append("</i></td></tr>\n");
}
out.append("\t\t\t\t\t\t<tr><td align=\"left\" port=\"");
out.append(field.name());
out.append("\"><b> + ");
out.append(field.name());
out.append("</b>: ");
out.append(generateClassModelTypescript(fieldModel, dotGroup));
final String remoteType = generateClassModelTypescriptLink(fieldModel, dotGroup);
if (remoteType != null) {
outLinks.append("\t");
outLinks.append(this.dotTypeName);
outLinks.append(":");
outLinks.append(field.name());
outLinks.append(":e -> ");
outLinks.append(remoteType);
outLinks.append(":NAME:w\n");
} else if (field.linkClass() != null) {
final String remoteLinkType = generateClassModelTypescriptLink(field.linkClass(), dotGroup);
if (remoteLinkType != null) {
outLinks.append("\t");
outLinks.append(this.dotTypeName);
outLinks.append(":");
outLinks.append(field.name());
outLinks.append(":e -> ");
outLinks.append(remoteLinkType);
outLinks.append(":NAME:w\n");
}
}
out.append("</td></tr>\n");
}
out.append("""
</table>
</td>
</tr>
</table>>
]
""");
if (inheritence != null) {
out.append("\tedge [dir=back arrowtail=empty arrowsize=2]\n");
out.append("\t");
out.append(inheritence);
// heritage stop link on the "s" South
out.append(":s -> ");
out.append(this.dotTypeName);
// heritage start link on the "n" North
out.append(":n\n");
}
if (!outLinks.isEmpty()) {
out.append("\tedge [dir=back arrowtail=diamond arrowsize=2]\n");
//out.append("\tedge [arrowhead=diamond arrowsize=2]\n");
out.append(outLinks.toString());
}
return out.toString();
}
private static String generateDotMap(final ClassMapModel model, final DotClassElementGroup dotGroup) {
final StringBuilder out = new StringBuilder();
out.append("Map&lt;");
if (model.keyModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateDotEnum(fieldEnumModel, dotGroup);
out.append(tmp);
}
out.append(", ");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateDotEnum(fieldEnumModel, dotGroup);
out.append(tmp);
}
out.append("&gt;");
return out.toString();
}
private static String generateDotEnum(final ClassEnumModel model, final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
return dotParentModel.dotTypeName;
}
private static String generateDotObject(final ClassObjectModel model, final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
return dotParentModel.dotTypeName;
}
private static String generateDotObjectClassName(
final ClassObjectModel model,
final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
if (dotParentModel.nativeType == DefinedPosition.NORMAL) {
return dotParentModel.dotTypeName;
}
return null;
}
private static String generateDotListClassName(final ClassListModel model, final DotClassElementGroup dotGroup) {
if (model.valueModel instanceof final ClassListModel fieldListModel) {
return generateDotListClassName(fieldListModel, dotGroup);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
return generateDotMapClassName(fieldMapModel, dotGroup);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
return generateDotObjectClassName(fieldObjectModel, dotGroup);
}
return null;
}
private static String generateDotMapClassName(final ClassMapModel model, final DotClassElementGroup dotGroup) {
if (model.valueModel instanceof final ClassListModel fieldListModel) {
return generateDotListClassName(fieldListModel, dotGroup);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
return generateDotMapClassName(fieldMapModel, dotGroup);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
return generateDotObjectClassName(fieldObjectModel, dotGroup);
} else if (model.valueModel instanceof final ClassEnumModel fieldEnumModel) {
return generateDotEnum(fieldEnumModel, dotGroup);
}
return null;
}
private static String generateDotList(final ClassListModel model, final DotClassElementGroup dotGroup) {
final StringBuilder out = new StringBuilder();
out.append("List&lt;");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
}
out.append("&gt;");
return out.toString();
}
public String generateFile(final DotClassElementGroup dotGroup) throws IOException {
if (this.nativeType == DefinedPosition.NATIVE) {
return "";
}
final ClassModel model = this.models.get(0);
String data = "";
if (this.nativeType == DefinedPosition.BASIC && model instanceof ClassObjectModel) {
// nothing to do___ data = generateBaseObject();
} else if (model instanceof final ClassEnumModel modelEnum) {
data = generateEnum(modelEnum, dotGroup);
} else if (model instanceof final ClassObjectModel modelObject) {
data = generateObject(modelObject, dotGroup);
}
return data;
}
private static String generateLocalModelBase(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateDotObject(objectModel, dotGroup);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateDotEnum(enumModel, dotGroup);
}
if (model instanceof final ClassListModel listModel) {
return generateDotList(listModel, dotGroup);
}
if (model instanceof final ClassMapModel mapModel) {
return generateDotMap(mapModel, dotGroup);
}
return "";
}
public static String generateLocalModel(
final String ModelName,
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 1) {
if (models.get(0) instanceof ClassObjectModel) {
return null;
}
if (models.get(0) instanceof ClassEnumModel) {
return null;
}
}
final StringBuilder out = new StringBuilder();
if (models.size() == 1) {
out.append(generateLocalModelBase(models.get(0), dotGroup));
} else {
out.append("Union&lt;");
for (final ClassModel model : models) {
out.append("\t");
out.append(generateLocalModelBase(models.get(0), dotGroup));
out.append(",\n");
}
out.append("&gt;");
}
return out.toString();
}
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.externalRestApi.dot;
import java.util.List;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class DotClassElementGroup {
private final List<DotClassElement> dotElements;
public List<DotClassElement> getDotElements() {
return this.dotElements;
}
public DotClassElementGroup(final List<DotClassElement> tsElements) {
this.dotElements = tsElements;
}
public DotClassElement find(final ClassModel model) {
for (final DotClassElement elem : this.dotElements) {
if (elem.isCompatible(model)) {
return elem;
}
}
return null;
}
}

View File

@@ -0,0 +1,72 @@
package org.kar.archidata.externalRestApi.model;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// Temporary class that permit to order the list of API.
record OrderedElement(
String methodName,
Method method) {}
public class ApiGroupModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiGroupModel.class);
// Name of the REST end-point name
public String restEndPoint;
// Name of the Class
public String name;
// Origin class reference
public Class<?> originClass;
// List of all API
public List<ApiModel> interfaces = new ArrayList<>();
public ApiModel getInterfaceNamed(final String name) {
for (final ApiModel model : this.interfaces) {
if (name.equals(model.name)) {
return model;
}
}
return null;
}
public ApiGroupModel(final Class<?> clazz, final ModelGroup previousModel) throws Exception {
this.originClass = clazz;
// the basic path has no specific elements...
this.restEndPoint = ApiTool.apiAnnotationGetPath(clazz);
if (this.restEndPoint == null) {
this.restEndPoint = "";
}
this.name = clazz.getSimpleName();
final List<String> consumes = ApiTool.apiAnnotationGetConsumes(clazz);
final List<String> produces = ApiTool.apiAnnotationProduces(clazz);
// Get all method to order it. This permit to stabilize the generation.
// JAVA has dynamic allocation of member order, then we need to order it?.
final List<OrderedElement> orderedElements = new ArrayList<>();
for (final Method method : clazz.getDeclaredMethods()) {
final String methodName = method.getName();
orderedElements.add(new OrderedElement(methodName, method));
}
final Comparator<OrderedElement> comparator = Comparator.comparing(OrderedElement::methodName);
Collections.sort(orderedElements, comparator);
for (final OrderedElement orderedElement : orderedElements) {
// Check if the path is available
final RestTypeRequest methodType = ApiTool.apiAnnotationGetTypeRequest2(orderedElement.method());
if (methodType == null) {
LOGGER.info(" [{}] {} ==> No methode type @PATH, @GET ...", methodType, orderedElement.methodName());
continue;
}
final ApiModel model = new ApiModel(clazz, orderedElement.method(), this.restEndPoint, consumes, produces,
previousModel);
this.interfaces.add(model);
}
}
}

View File

@@ -0,0 +1,176 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiModel.class);
Class<?> originClass;
Method orignMethod;
// Name of the REST end-point name
public String restEndPoint;
// Type of the request:
public RestTypeRequest restTypeRequest;
// Description of the API
public String description;
// need to generate the progression of stream (if possible)
public boolean needGenerateProgress;
// List of types returned by the API
public List<ClassModel> returnTypes = new ArrayList<>();;
// Name of the API (function name)
public String name;
// list of all parameters (/{key}/...
public final Map<String, List<ClassModel>> parameters = new HashMap<>();
// list of all query (?key...)
public final Map<String, List<ClassModel>> queries = new HashMap<>();
// when request multi-part, need to separate it.
public final Map<String, List<ClassModel>> multiPartParameters = new HashMap<>();
// model of data available
public final List<ClassModel> unnamedElement = new ArrayList<>();
// Possible input type of the REST API
public List<String> consumes = new ArrayList<>();
// Possible output type of the REST API
public List<String> produces = new ArrayList<>();
private void updateReturnTypes(final Method method, final ModelGroup previousModel) throws Exception {
// get return type from the user specification:
final Class<?>[] returnTypeModel = ApiTool.apiAnnotationGetAsyncType(method);
LOGGER.info("Get return Type async = {}", returnTypeModel);
if (returnTypeModel != null) {
if (returnTypeModel.length == 0) {
throw new IOException("Create a @AsyncType with empty elements ...");
}
for (final Class<?> clazz : returnTypeModel) {
if (clazz == Void.class || clazz == void.class) {
this.returnTypes.add(previousModel.add(Void.class));
} else if (clazz == List.class) {
throw new IOException("Unmanaged List.class in @AsyncType.");
} else if (clazz == Map.class) {
throw new IOException("Unmanaged Map.class in @AsyncType.");
} else {
this.returnTypes.add(previousModel.add(clazz));
}
}
if (this.returnTypes.size() == 0) {
this.produces.clear();
}
return;
}
final Class<?> returnTypeModelRaw = method.getReturnType();
LOGGER.info("Get return Type RAW = {}", returnTypeModelRaw.getCanonicalName());
if (returnTypeModelRaw == Map.class) {
LOGGER.warn("Model Map");
final Type listType = method.getGenericReturnType();
final ClassModel modelGenerated = ClassModel.getModel(listType, previousModel);
this.returnTypes.add(modelGenerated);
LOGGER.warn("Model Map ==> {}", modelGenerated);
return;
} else if (returnTypeModelRaw == List.class) {
LOGGER.warn("Model List");
final Type listType = method.getGenericReturnType();
final ClassModel modelGenerated = ClassModel.getModel(listType, previousModel);
this.returnTypes.add(modelGenerated);
LOGGER.warn("Model List ==> {}", modelGenerated);
return;
} else {
LOGGER.warn("Model Object");
this.returnTypes.add(previousModel.add(returnTypeModelRaw));
}
LOGGER.warn("List of returns elements:");
for (final ClassModel elem : this.returnTypes) {
LOGGER.warn(" - {}", elem);
}
if (this.returnTypes.size() == 0) {
this.produces.clear();
}
}
public ApiModel(final Class<?> clazz, final Method method, final String baseRestEndPoint,
final List<String> consume, final List<String> produce, final ModelGroup previousModel) throws Exception {
this.originClass = clazz;
this.orignMethod = method;
String tmpPath = ApiTool.apiAnnotationGetPath(method);
if (tmpPath == null) {
tmpPath = "";
}
this.restEndPoint = baseRestEndPoint + "/" + tmpPath;
this.restTypeRequest = ApiTool.apiAnnotationGetTypeRequest2(method);
this.name = method.getName();
this.description = ApiTool.apiAnnotationGetOperationDescription(method);
this.consumes = ApiTool.apiAnnotationGetConsumes2(consume, method);
this.produces = ApiTool.apiAnnotationProduces2(produce, method);
LOGGER.trace(" [{}] {} => {}/{}", baseRestEndPoint, this.name, this.restEndPoint);
this.needGenerateProgress = ApiTool.apiAnnotationTypeScriptProgress(method);
updateReturnTypes(method, previousModel);
LOGGER.trace(" return: {}", this.returnTypes.size());
for (final ClassModel elem : this.returnTypes) {
LOGGER.trace(" - {}", elem);
}
// LOGGER.info(" Parameters:");
for (final Parameter parameter : method.getParameters()) {
// Security context are internal parameter (not available from API)
if (ApiTool.apiAnnotationIsContext(parameter)) {
continue;
}
final Class<?> parameterType = parameter.getType();
final List<ClassModel> parameterModel = new ArrayList<>();
final Class<?>[] asyncType = ApiTool.apiAnnotationGetAsyncType(parameter);
if (asyncType != null) {
for (final Class<?> elem : asyncType) {
final ClassModel modelGenerated = ClassModel.getModel(elem, previousModel);
parameterModel.add(modelGenerated);
}
} else if (parameterType == List.class) {
final Type parameterrizedType = parameter.getParameterizedType();
final ClassModel modelGenerated = ClassModel.getModel(parameterrizedType, previousModel);
parameterModel.add(modelGenerated);
} else if (parameterType == Map.class) {
final Type parameterrizedType = parameter.getParameterizedType();
final ClassModel modelGenerated = ClassModel.getModel(parameterrizedType, previousModel);
parameterModel.add(modelGenerated);
} else {
parameterModel.add(previousModel.add(parameterType));
}
final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter);
if (queryParam != null) {
if (!this.queries.containsKey(queryParam)) {
this.queries.put(queryParam, parameterModel);
}
} else if (pathParam != null) {
if (!this.parameters.containsKey(pathParam)) {
this.parameters.put(pathParam, parameterModel);
}
} else if (formDataParam != null) {
if (!this.multiPartParameters.containsKey(formDataParam)) {
this.multiPartParameters.put(formDataParam, parameterModel);
}
} else {
this.unnamedElement.addAll(parameterModel);
}
}
if (this.unnamedElement.size() > 1) {
throw new IOException("Can not parse the API, enmpty element is more than 1 in " + this.name);
}
}
}

View File

@@ -0,0 +1,208 @@
package org.kar.archidata.externalRestApi.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
public class ApiTool {
public static String apiAnnotationGetPath(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static List<String> apiAnnotationProduces(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static List<String> apiAnnotationProduces(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static boolean apiAnnotationTypeScriptProgress(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(TypeScriptProgress.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static List<String> apiAnnotationProduces(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationProduces(method);
if (data != null) {
return data;
}
return apiAnnotationProduces(clazz);
}
public static List<String> apiAnnotationProduces2(final List<String> parentProduce, final Method method)
throws Exception {
final List<String> data = apiAnnotationProduces(method);
if (data != null) {
return data;
}
return parentProduce;
}
public static String apiAnnotationGetOperationDescription(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Operation.class);
if (annotation.length == 0) {
return null;
}
return ((Operation) annotation[0]).description();
}
public static String apiAnnotationGetPath(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static String apiAnnotationGetTypeRequest(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return "GET";
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return "POST";
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return "PUT";
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return "PATCH";
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return "DELETE";
}
return null;
}
public static RestTypeRequest apiAnnotationGetTypeRequest2(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return RestTypeRequest.GET;
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return RestTypeRequest.POST;
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return RestTypeRequest.PUT;
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return RestTypeRequest.PATCH;
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return RestTypeRequest.DELETE;
}
return null;
}
public static String apiAnnotationGetPathParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(PathParam.class);
if (annotation.length == 0) {
return null;
}
return ((PathParam) annotation[0]).value();
}
public static String apiAnnotationGetQueryParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(QueryParam.class);
if (annotation.length == 0) {
return null;
}
return ((QueryParam) annotation[0]).value();
}
public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class);
if (annotation.length == 0) {
return null;
}
return ((FormDataParam) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
}
public static List<String> apiAnnotationGetConsumes(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return apiAnnotationGetConsumes(clazz);
}
public static List<String> apiAnnotationGetConsumes2(final List<String> parentConsume, final Method method)
throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return parentConsume;
}
public static boolean apiAnnotationIsContext(final Parameter element) throws Exception {
return element.getDeclaredAnnotationsByType(Context.class).length != 0;
}
}

View File

@@ -0,0 +1,67 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ClassEnumModel extends ClassModel {
protected ClassEnumModel(final Class<?> clazz) {
this.originClasses = clazz;
this.noWriteSpecificMode = true;
}
@Override
public String toString() {
final StringBuilder out = new StringBuilder();
out.append("ClassEnumModel [");
out.append(this.originClasses.getCanonicalName());
out.append("]");
return out.toString();
}
final Map<String, Object> listOfValues = new HashMap<>();
@Override
public void analyze(final ModelGroup group) throws IOException {
if (this.analyzeDone) {
return;
}
this.analyzeDone = true;
final Class<?> clazz = this.originClasses;
final Object[] constants = clazz.getEnumConstants();
// Try to get a get Value element to serialize:
try {
final Method getValueMethod = clazz.getMethod("getValue");
for (final Object constant : constants) {
final String name = constant.toString();
final Object value = getValueMethod.invoke(constant);
this.listOfValues.put(name, value);
}
return;
} catch (final Exception e) {
//e.printStackTrace();
}
for (final Object elem : constants) {
this.listOfValues.put(elem.toString(), elem.toString());
}
}
public Map<String, Object> getListOfValues() {
return this.listOfValues;
}
@Override
public Set<ClassModel> getAlls() {
return Set.of(this);
}
@Override
public Set<ClassModel> getDependencyGroupModels() {
return Set.of(this);
}
}

View File

@@ -0,0 +1,47 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Set;
public class ClassListModel extends ClassModel {
public ClassModel valueModel;
public ClassListModel(final ClassModel valueModel) {
this.valueModel = valueModel;
}
public ClassListModel(final Class<?> clazz, final ModelGroup previousModel) throws IOException {
this.valueModel = getModel(clazz, previousModel);
}
public ClassListModel(final Type model, final ModelGroup previousModel) throws IOException {
this.valueModel = getModel(model, previousModel);
}
public ClassListModel(final ParameterizedType listType, final ModelGroup previousModel) throws IOException {
final Type model = listType.getActualTypeArguments()[0];
this.valueModel = getModel(model, previousModel);
}
@Override
public String toString() {
return "ClassListModel [valueModel=" + this.valueModel + "]";
}
@Override
public void analyze(final ModelGroup group) throws IOException {
throw new IOException("Analyze can not be done at this phase for List...");
}
@Override
public Set<ClassModel> getAlls() {
return this.valueModel.getAlls();
}
@Override
public Set<ClassModel> getDependencyGroupModels() {
return this.valueModel.getDependencyGroupModels();
}
}

View File

@@ -0,0 +1,52 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
public class ClassMapModel extends ClassModel {
public ClassModel keyModel;
public ClassModel valueModel;
public ClassMapModel(final ClassModel keyModel, final ClassModel valueModel) {
this.keyModel = keyModel;
this.valueModel = valueModel;
}
public ClassMapModel(final Type listTypeKey, final Type listTypeValue, final ModelGroup previousModel)
throws IOException {
this.keyModel = getModel(listTypeKey, previousModel);
this.valueModel = getModel(listTypeValue, previousModel);
}
public ClassMapModel(final ParameterizedType listType, final ModelGroup previousModel) throws IOException {
this.keyModel = getModel(listType.getActualTypeArguments()[0], previousModel);
this.valueModel = getModel(listType.getActualTypeArguments()[1], previousModel);
}
@Override
public String toString() {
return "ClassMapModel [keyModel=" + this.keyModel + ", valueModel=" + this.valueModel + "]";
}
@Override
public void analyze(final ModelGroup group) throws IOException {
throw new IOException("Analyze can not be done at this phase for Map...");
}
@Override
public Set<ClassModel> getAlls() {
final Set<ClassModel> out = new HashSet<>(this.keyModel.getAlls());
out.addAll(this.valueModel.getAlls());
return out;
}
@Override
public Set<ClassModel> getDependencyGroupModels() {
final Set<ClassModel> out = new HashSet<>(this.valueModel.getDependencyGroupModels());
out.addAll(this.keyModel.getDependencyGroupModels());
return out;
}
}

View File

@@ -0,0 +1,83 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class ClassModel {
protected boolean analyzeDone = false;
protected Class<?> originClasses = null;
protected boolean noWriteSpecificMode = false;
protected List<ClassModel> dependencyModels = new ArrayList<>();
public Class<?> getOriginClasses() {
return this.originClasses;
}
public boolean isNoWriteSpecificMode() {
return this.noWriteSpecificMode;
}
protected boolean isCompatible(final Class<?> clazz) {
return this.originClasses == clazz;
}
public List<ClassModel> getDependencyModels() {
return this.dependencyModels;
}
public abstract Set<ClassModel> getDependencyGroupModels();
public static ClassModel getModel(final Type type, final ModelGroup previousModel) throws IOException {
if (type instanceof final ParameterizedType paramType) {
final Type[] typeArguments = paramType.getActualTypeArguments();
if (paramType.getRawType() == List.class) {
return new ClassListModel(typeArguments[0], previousModel);
}
if (paramType.getRawType() == Map.class) {
return new ClassMapModel(typeArguments[0], typeArguments[1], previousModel);
}
throw new IOException("Fail to manage parametrized type...");
}
return previousModel.add((Class<?>) type);
}
public static ClassModel getModelBase(
final Class<?> clazz,
final Type parameterizedType,
final ModelGroup previousModel) throws IOException {
/*
if (clazz == List.class) {
return new ClassListModel((ParameterizedType) parameterizedType, previousModel);
}
if (clazz == Map.class) {
return new ClassMapModel((ParameterizedType) parameterizedType, previousModel);
}
return previousModel.add(clazz);
*/
return getModel(parameterizedType, previousModel);
}
public static ClassModel getModel(final Class<?> type, final ModelGroup previousModel) throws IOException {
if (type == List.class) {
throw new IOException("Fail to manage parametrized type...");
}
if (type == Map.class) {
throw new IOException("Fail to manage parametrized type...");
}
return previousModel.add(type);
}
public abstract void analyze(final ModelGroup group) throws Exception;
public abstract Set<ClassModel> getAlls();
public List<String> getReadOnlyField() {
return List.of();
}
}

View File

@@ -0,0 +1,270 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.Size;
public class ClassObjectModel extends ClassModel {
static final Logger LOGGER = LoggerFactory.getLogger(ClassObjectModel.class);
public ClassObjectModel(final Class<?> clazz) {
this.originClasses = clazz;
}
@Override
public String toString() {
final StringBuilder out = new StringBuilder();
out.append("ClassObjectModel [");
out.append(this.originClasses.getCanonicalName());
out.append("]");
return out.toString();
}
private static boolean isFieldFromSuperClass(final Class<?> model, final String filedName) {
final Class<?> superClass = model.getSuperclass();
if (superClass == null) {
return false;
}
for (final Field field : superClass.getFields()) {
String name;
try {
name = AnnotationTools.getFieldName(field);
if (filedName.equals(name)) {
return true;
}
} catch (final Exception e) {
// TODO Auto-generated catch block
LOGGER.trace("Catch error field name in parent create data table: {}", e.getMessage());
}
}
return false;
}
public record FieldProperty(
String name,
ClassModel model,
ClassModel linkClass, // link class when use remote ID (ex: list<UUID>)
String comment,
int sizeMin, // String SizeMin
int sizeMax, // String SizeMax
Long min, // number min value
Long max, // number max value
Boolean readOnly,
Boolean notNull,
Boolean columnNotNull,
Boolean nullable) {
public FieldProperty(final String name, final ClassModel model, final ClassModel linkClass,
final String comment, final int sizeMin, final int sizeMax, final Long min, final Long max,
final Boolean readOnly, final Boolean notNull, final Boolean columnNotNull, final Boolean nullable) {
this.name = name;
this.model = model;
this.linkClass = linkClass;
this.comment = comment;
this.sizeMin = sizeMin;
this.sizeMax = sizeMax;
this.min = min;
this.max = max;
this.readOnly = readOnly;
this.notNull = notNull;
this.columnNotNull = columnNotNull;
this.nullable = nullable;
}
private static int getStringMinSize(final Field field) throws DataAccessException {
final Size size = AnnotationTools.getConstraintsSize(field);
return size != null ? size.min() : 0;
}
private static int getStringMaxSize(final Field field) throws DataAccessException {
final Size size = AnnotationTools.getConstraintsSize(field);
final int colomnLimitSize = AnnotationTools.getLimitSize(field);
return size == null ? colomnLimitSize : colomnLimitSize < size.max() ? colomnLimitSize : size.max();
}
private static Class<?> getSubModelIfExist2(final Field field) {
final ManyToOne manyToOne = AnnotationTools.getManyToOne(field);
if (manyToOne != null) {
if (manyToOne.targetEntity() != null && manyToOne.targetEntity() != void.class) {
return manyToOne.targetEntity();
}
return null;
}
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
if (manyToMany != null) {
if (manyToMany.targetEntity() != null && manyToMany.targetEntity() != void.class) {
return manyToMany.targetEntity();
}
return null;
}
final OneToMany oneToMany = AnnotationTools.getOneToMany(field);
if (oneToMany != null) {
if (oneToMany.targetEntity() != null && oneToMany.targetEntity() != void.class) {
return oneToMany.targetEntity();
}
return null;
}
return null;
}
private static ClassModel getSubModelIfExist(final Field field, final ModelGroup previous) throws IOException {
final Class<?> tmp = getSubModelIfExist2(field);
if (tmp == null) {
return null;
}
return ClassModel.getModel(tmp, previous);
}
public FieldProperty(final Field field, final ModelGroup previous) throws DataAccessException, IOException {
this(field.getName(), //
ClassModel.getModel(field.getGenericType(), previous), //
getSubModelIfExist(field, previous), //
AnnotationTools.getComment(field), //
getStringMinSize(field), //
getStringMaxSize(field), //
AnnotationTools.getConstraintsMin(field), //
AnnotationTools.getConstraintsMax(field), //
AnnotationTools.getSchemaReadOnly(field), //
AnnotationTools.getConstraintsNotNull(field), //
AnnotationTools.getColumnNotNull(field), //
AnnotationTools.getNullable(field));
}
}
String name = "";
boolean isPrimitive = false;
String description = null;
String example = null;
ClassModel extendsClass = null;
List<FieldProperty> fields = new ArrayList<>();
public String getName() {
return this.name;
}
public boolean isPrimitive() {
return this.isPrimitive;
}
public String getDescription() {
return this.description;
}
public String getExample() {
return this.example;
}
public ClassModel getExtendsClass() {
return this.extendsClass;
}
public List<FieldProperty> getFields() {
return this.fields;
}
@Override
public void analyze(final ModelGroup previous) throws Exception {
if (this.analyzeDone) {
return;
}
this.analyzeDone = true;
final Class<?> clazz = this.originClasses;
this.noWriteSpecificMode = AnnotationTools.getNoWriteSpecificMode(clazz);
this.isPrimitive = clazz.isPrimitive();
if (this.isPrimitive) {
return;
}
final List<Class<?>> basicClass = List.of(Void.class, void.class, Character.class, char.class, Short.class,
short.class, Integer.class, int.class, Long.class, long.class, Float.class, float.class, Double.class,
double.class, Date.class, Timestamp.class, LocalDate.class, LocalTime.class);
if (basicClass.contains(clazz)) {
return;
}
// Local generation of class:
LOGGER.trace("parse class: '{}'", clazz.getCanonicalName());
final List<String> alreadyAdded = new ArrayList<>();
for (final Field elem : clazz.getFields()) {
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
continue;
}
final String dataName = elem.getName();
if (isFieldFromSuperClass(clazz, dataName)) {
LOGGER.trace(" SKIP: '{}'", elem.getName());
continue;
}
if (alreadyAdded.contains(dataName)) {
LOGGER.trace(" SKIP2: '{}'", elem.getName());
continue;
}
alreadyAdded.add(dataName);
LOGGER.trace(" + '{}'", elem.getName());
LOGGER.trace("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType());
final FieldProperty porperty = new FieldProperty(elem, previous);
for (final ClassModel depModel : porperty.model().getAlls()) {
if (!this.dependencyModels.contains(depModel)) {
this.dependencyModels.add(depModel);
}
}
this.fields.add(new FieldProperty(elem, previous));
}
this.name = clazz.getName();
final String[] elems = this.name.split("\\$");
if (elems.length == 2) {
LOGGER.warn("Can have conflict in generation: {} (Remove class path) ==> {}", this.name, elems[1]);
this.name = elems[1];
}
this.description = AnnotationTools.getSchemaDescription(clazz);
this.example = AnnotationTools.getSchemaExample(clazz);
final Class<?> parentClass = clazz.getSuperclass();
// manage heritage
if (parentClass != null && parentClass != Object.class && parentClass != Record.class) {
this.extendsClass = previous.add(parentClass);
this.dependencyModels.add(this.extendsClass);
}
}
@Override
public Set<ClassModel> getDependencyGroupModels() {
return Set.of(this);
}
@Override
public Set<ClassModel> getAlls() {
return Set.of(this);
}
@Override
public List<String> getReadOnlyField() {
final List<String> out = new ArrayList<>();
for (final FieldProperty field : this.fields) {
if (field.readOnly()) {
out.add(field.name);
}
}
if (this.extendsClass != null) {
out.addAll(this.extendsClass.getReadOnlyField());
}
return out;
}
}

View File

@@ -0,0 +1,53 @@
package org.kar.archidata.externalRestApi.model;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response;
public class ModelGroup {
static final Logger LOGGER = LoggerFactory.getLogger(ModelGroup.class);
public List<ClassModel> models = new ArrayList<>();
public ModelGroup() {}
public void addAll(final List<Class<?>> classes) {
for (final Class<?> clazz : classes) {
add(clazz);
}
}
public ClassModel add(Class<?> clazz) {
if (clazz == Response.class) {
clazz = Object.class;
}
if (clazz == Number.class) {
return null;
}
//LOGGER.trace("Search element {}", clazz.getCanonicalName());
for (final ClassModel value : this.models) {
if (value.isCompatible(clazz)) {
//LOGGER.trace(" ==> return {}", value);
return value;
}
}
if (clazz.isEnum()) {
final ClassModel elem = new ClassEnumModel(clazz);
this.models.add(elem);
//LOGGER.trace(" ==> return enum {}", elem);
return elem;
}
// create new model:
final ClassModel elem = new ClassObjectModel(clazz);
this.models.add(elem);
//LOGGER.trace(" ==> return object {}", elem);
return elem;
}
public List<ClassModel> getModels() {
return this.models;
}
}

View File

@@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi.model;
public enum RestTypeRequest {
GET, POST, PUT, PATCH, DELETE
}

View File

@@ -0,0 +1,443 @@
package org.kar.archidata.externalRestApi.typescript;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import org.kar.archidata.dataAccess.DataExport;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ApiModel;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.kar.archidata.externalRestApi.model.RestTypeRequest;
import org.kar.archidata.externalRestApi.typescript.TsClassElement.DefinedPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.MediaType;
public class TsApiGeneration {
static final Logger LOGGER = LoggerFactory.getLogger(TsApiGeneration.class);
public static String getBaseHeader() {
return """
/**
* Interface of the server (auto-generated code)
*/
""";
}
public static String generateClassEnumModelTypescript(
final ClassEnumModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
imports.add(model);
final TsClassElement tsModel = tsGroup.find(model);
return tsModel.tsTypeName;
}
public static String generateClassObjectModelTypescript(
final ClassObjectModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NATIVE) {
if (importWrite == null || tsModel.models.get(0).isNoWriteSpecificMode()) {
imports.add(model);
} else {
importWrite.add(model);
}
}
if (tsModel.nativeType != DefinedPosition.NORMAL) {
return tsModel.tsTypeName;
}
if (importWrite != null && !tsModel.models.get(0).isNoWriteSpecificMode()) {
return tsModel.tsTypeName + "Write";
}
return tsModel.tsTypeName;
}
public static String generateClassMapModelTypescript(
final ClassMapModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("{[key: ");
out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, importWrite));
out.append("]: ");
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append(";}");
return out.toString();
}
public static String generateClassListModelTypescript(
final ClassListModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append("[]");
return out.toString();
}
public static String generateClassModelTypescript(
final ClassModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, tsGroup, imports, importWrite);
}
if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, tsGroup, imports, importWrite);
}
if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, tsGroup, imports, importWrite);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, tsGroup, imports, importWrite);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
if (models.size() == 0) {
return "void";
}
final StringBuilder out = new StringBuilder();
boolean isFirst = true;
for (final ClassModel model : models) {
if (isFirst) {
isFirst = false;
} else {
out.append(" | ");
}
final String data = generateClassModelTypescript(model, tsGroup, imports, importWrite);
out.append(data);
}
return out.toString();
}
public static String capitalizeFirstLetter(final String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
public static void generateApiFile(
final ApiGroupModel element,
final String pathPackage,
final TsClassElementGroup tsGroup) throws IOException {
final StringBuilder data = new StringBuilder();
data.append("export namespace ");
data.append(element.name);
data.append(" {\n");
final Set<ClassModel> imports = new HashSet<>();
final Set<ClassModel> zodImports = new HashSet<>();
final Set<ClassModel> isImports = new HashSet<>();
final Set<ClassModel> writeImports = new HashSet<>();
final Set<String> toolImports = new HashSet<>();
for (final ApiModel interfaceElement : element.interfaces) {
final List<String> consumes = interfaceElement.consumes;
final List<String> produces = interfaceElement.produces;
final boolean needGenerateProgress = interfaceElement.needGenerateProgress;
final String returnModelNameIfComplex = capitalizeFirstLetter(interfaceElement.name) + "TypeReturn";
final String returnComplexModel = TsClassElement.generateLocalModel(returnModelNameIfComplex,
interfaceElement.returnTypes, tsGroup);
if (returnComplexModel != null) {
data.append("\n\n");
data.append(returnComplexModel.replaceAll("(?m)^", "\t"));
for (final ClassModel elem : interfaceElement.returnTypes) {
zodImports.addAll(elem.getDependencyGroupModels());
}
}
if (interfaceElement.description != null) {
data.append("\n\t/**\n\t * ");
data.append(interfaceElement.description);
data.append("\n\t */");
}
data.append("\n\texport function ");
data.append(interfaceElement.name);
data.append("({\n\t\t\trestConfig,");
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\t\tqueries,");
}
if (!interfaceElement.parameters.isEmpty()) {
data.append("\n\t\t\tparams,");
}
if (produces != null && produces.size() > 1) {
data.append("\n\t\t\tproduce,");
}
if (interfaceElement.unnamedElement.size() == 1 || interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
data.append("\n\t\t\tcallbacks,");
}
data.append("\n\t\t}: {");
data.append("\n\t\trestConfig: RESTConfig,");
toolImports.add("RESTConfig");
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\tqueries: {");
for (final Entry<String, List<ClassModel>> queryEntry : interfaceElement.queries.entrySet()) {
data.append("\n\t\t\t");
data.append(queryEntry.getKey());
data.append("?: ");
data.append(generateClassModelsTypescript(queryEntry.getValue(), tsGroup, imports, null));
data.append(",");
}
data.append("\n\t\t},");
}
if (!interfaceElement.parameters.isEmpty()) {
data.append("\n\t\tparams: {");
for (final Entry<String, List<ClassModel>> paramEntry : interfaceElement.parameters.entrySet()) {
data.append("\n\t\t\t");
data.append(paramEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(paramEntry.getValue(), tsGroup, imports, null));
data.append(",");
}
data.append("\n\t\t},");
}
if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\tdata: ");
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
writeImports));
data.append(",");
} else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\tdata: {");
for (final Entry<String, List<ClassModel>> pathEntry : interfaceElement.multiPartParameters
.entrySet()) {
data.append("\n\t\t\t");
data.append(pathEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue(), tsGroup, imports, writeImports));
data.append(",");
}
data.append("\n\t\t},");
}
if (produces != null && produces.size() > 1) {
data.append("\n\t\tproduce: ");
String isFist = null;
for (final String elem : produces) {
String lastElement = null;
if (MediaType.APPLICATION_JSON.equals(elem)) {
lastElement = "HTTPMimeType.JSON";
toolImports.add("HTTPMimeType");
}
if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
lastElement = "HTTPMimeType.MULTIPART";
toolImports.add("HTTPMimeType");
}
if (DataExport.CSV_TYPE.equals(elem)) {
lastElement = "HTTPMimeType.CSV";
toolImports.add("HTTPMimeType");
}
if (lastElement != null) {
if (isFist == null) {
isFist = lastElement;
} else {
data.append(" | ");
}
data.append(lastElement);
} else {
LOGGER.error("Unmanaged model type: {}", elem);
}
}
data.append(",");
}
if (needGenerateProgress) {
data.append("\n\t\tcallbacks?: RESTCallbacks,");
toolImports.add("RESTCallbacks");
}
data.append("\n\t}): Promise<");
if (returnComplexModel != null) {
data.append(returnModelNameIfComplex);
data.append("> {");
data.append("\n\t\treturn RESTRequestJson({");
toolImports.add("RESTRequestJson");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, imports,
null);
data.append(returnType);
data.append("> {");
if ("void".equals(returnType)) {
data.append("\n\t\treturn RESTRequestVoid({");
toolImports.add("RESTRequestVoid");
} else {
isImports.addAll(interfaceElement.returnTypes);
data.append("\n\t\treturn RESTRequestJson({");
toolImports.add("RESTRequestJson");
}
}
data.append("\n\t\t\trestModel: {");
data.append("\n\t\t\t\tendPoint: \"");
data.append(interfaceElement.restEndPoint);
data.append("\",");
data.append("\n\t\t\t\trequestType: HTTPRequestModel.");
toolImports.add("HTTPRequestModel");
data.append(interfaceElement.restTypeRequest);
data.append(",");
if (consumes != null) {
for (final String elem : consumes) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.MULTIPART,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.TEXT_PLAIN.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
break;
}
}
} else if (RestTypeRequest.DELETE.equals(interfaceElement.restTypeRequest)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
}
if (produces != null) {
if (produces.size() > 1) {
data.append("\n\t\t\t\taccept: produce,");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup,
imports, null);
if (!"void".equals(returnType)) {
for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\taccept: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
}
}
}
}
}
data.append("\n\t\t\t},");
data.append("\n\t\t\trestConfig,");
if (!interfaceElement.parameters.isEmpty()) {
data.append("\n\t\t\tparams,");
}
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\t\tqueries,");
}
if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\t\tdata,");
} else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
data.append("\n\t\t\tcallbacks,");
}
data.append("\n\t\t}");
if (returnComplexModel != null) {
data.append(", is");
data.append(returnModelNameIfComplex);
} else {
final TsClassElement retType = tsGroup.find(interfaceElement.returnTypes.get(0));
if (retType.tsCheckType != null) {
data.append(", ");
data.append(retType.tsCheckType);
imports.add(interfaceElement.returnTypes.get(0));
}
}
data.append(");");
data.append("\n\t};");
}
data.append("\n}\n");
final StringBuilder out = new StringBuilder();
out.append(getBaseHeader());
final List<String> toolImportsList = new ArrayList<>(toolImports);
Collections.sort(toolImportsList);
if (toolImportsList.size() != 0) {
out.append("import {");
for (final String elem : toolImportsList) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../rest-tools\";\n\n");
}
if (zodImports.size() != 0) {
out.append("import { z as zod } from \"zod\"\n");
}
final Set<String> finalImportSet = new TreeSet<>();
for (final ClassModel model : imports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(tsModel.tsTypeName);
}
for (final ClassModel model : isImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
if (tsModel.tsCheckType != null) {
finalImportSet.add(tsModel.tsCheckType);
}
}
for (final ClassModel model : zodImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add("Zod" + tsModel.tsTypeName);
}
for (final ClassModel model : writeImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(tsModel.tsTypeName + "Write");
}
if (finalImportSet.size() != 0) {
out.append("import {");
for (final String elem : finalImportSet) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../model\";\n\n");
}
out.append(data.toString());
final String fileName = TsClassElement.determineFileName(element.name);
final FileWriter myWriter = new FileWriter(
pathPackage + File.separator + "api" + File.separator + fileName + ".ts");
myWriter.write(out.toString());
myWriter.close();
}
}

View File

@@ -0,0 +1,590 @@
package org.kar.archidata.externalRestApi.typescript;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map.Entry;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel.FieldProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TsClassElement {
static final Logger LOGGER = LoggerFactory.getLogger(TsClassElement.class);
public enum DefinedPosition {
NATIVE, // Native element of TS language.
BASIC, // basic wrapping for JAVA type.
NORMAL // Normal Object to interpret.
}
public List<ClassModel> models;
public String zodName;
public String tsTypeName;
public String tsCheckType;
public String declaration;
public String fileName = null;
public String comment = null;
public DefinedPosition nativeType = DefinedPosition.NORMAL;
public static String determineFileName(final String className) {
return className.replaceAll("([a-z])([A-Z])", "$1-$2").replaceAll("([A-Z])([A-Z][a-z])", "$1-$2").toLowerCase();
}
public TsClassElement(final List<ClassModel> model, final String zodName, final String tsTypeName,
final String tsCheckType, final String declaration, final DefinedPosition nativeType) {
this.models = model;
this.zodName = zodName;
this.tsTypeName = tsTypeName;
this.tsCheckType = tsCheckType;
this.declaration = declaration;
this.nativeType = nativeType;
this.fileName = determineFileName(tsTypeName);
}
public TsClassElement(final ClassModel model) {
this.models = List.of(model);
this.zodName = "Zod" + model.getOriginClasses().getSimpleName();
this.tsTypeName = model.getOriginClasses().getSimpleName();
this.tsCheckType = "is" + model.getOriginClasses().getSimpleName();
this.declaration = null;
this.fileName = determineFileName(this.tsTypeName);
}
public boolean isCompatible(final ClassModel model) {
return this.models.contains(model);
}
public String getBaseHeader() {
return """
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
""";
}
public String generateEnum(final ClassEnumModel model, final TsClassElementGroup tsGroup) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(getBaseHeader());
out.append("\n");
//out.append(generateComment(model));
if (System.getenv("ARCHIDATA_GENERATE_ZOD_ENUM") != null) {
boolean first = true;
out.append("export const ");
out.append(this.tsTypeName);
out.append(" = ");
out.append("zod.enum([");
for (final Entry<String, Object> elem : model.getListOfValues().entrySet()) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append("'");
out.append(elem.getKey());
out.append("'");
}
if (first) {
out.append("]}");
} else {
out.append("\n\t])");
}
out.append(";\n");
out.append(generateZodInfer(this.tsTypeName, this.zodName));
} else {
boolean first = true;
out.append("export enum ");
out.append(this.tsTypeName);
out.append(" {");
for (final Entry<String, Object> elem : model.getListOfValues().entrySet()) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append(elem.getKey());
out.append(" = ");
if (elem.getValue() instanceof final Integer value) {
out.append(value);
} else {
out.append("'");
out.append(elem.getValue());
out.append("'");
}
}
if (first) {
out.append("}");
} else {
out.append(",\n\t}");
}
out.append(";\n");
out.append("\nexport const ");
out.append(this.zodName);
out.append(" = zod.nativeEnum(");
out.append(this.tsTypeName);
out.append(");\n");
}
out.append(generateExportCheckFunctionWrite(""));
return out.toString();
}
private static String generateExportCheckFunction(
final String tsCheckType,
final String tsTypeName,
final String zodName) {
final StringBuilder out = new StringBuilder();
out.append("\nexport function ");
out.append(tsCheckType);
out.append("(data: any): data is ");
out.append(tsTypeName);
out.append(" {\n\ttry {\n\t\t");
out.append(zodName);
out.append("""
.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='""");
out.append(zodName);
out.append("""
' error=${e}`);
return false;
}
}
""");
return out.toString();
}
private String generateExportCheckFunctionWrite(final String writeString) {
return generateExportCheckFunction(this.tsCheckType + writeString, this.tsTypeName + writeString,
this.zodName + writeString);
}
public String generateImports(final List<ClassModel> depModels, final TsClassElementGroup tsGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
for (final ClassModel depModel : depModels) {
final TsClassElement tsModel = tsGroup.find(depModel);
if (tsModel.nativeType != DefinedPosition.NATIVE) {
out.append("import {");
out.append(tsModel.zodName);
if (tsModel.nativeType == DefinedPosition.NORMAL && !(tsModel.models.get(0).isNoWriteSpecificMode())) {
out.append(", ");
out.append(tsModel.zodName);
out.append("Write ");
}
out.append("} from \"./");
out.append(tsModel.fileName);
out.append("\";\n");
}
}
return out.toString();
}
private Object generateComment(final ClassObjectModel model) {
final StringBuilder out = new StringBuilder();
if (model.getDescription() != null || model.getExample() != null) {
out.append("/**\n");
if (model.getDescription() != null) {
for (final String elem : model.getDescription().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
}
if (model.getExample() != null) {
out.append(" * Example:\n");
out.append(" * ```\n");
for (final String elem : model.getExample().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
out.append(" * ```\n");
}
out.append(" */\n");
}
return out.toString();
}
public boolean isOptionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (field.nullable()) {
return true;
}
if (field.notNull()) {
return false;
}
// Other object:
if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) {
return false;
}
if (field.columnNotNull()) {
return false;
}
return true;
}
public String optionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (isOptionalTypeZod(field)) {
return ".optional()";
}
return "";
}
public String optionalWriteTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (isOptionalTypeZod(field)) {
return ".nullable()";
}
return "";
}
public String maxSizeZod(final FieldProperty field) {
final StringBuilder builder = new StringBuilder();
final Class<?> clazz = field.model().getOriginClasses();
if (clazz == String.class) {
if (field.sizeMin() > 0) {
builder.append(".min(");
builder.append(field.sizeMin());
builder.append(")");
}
if (field.sizeMax() > 0) {
builder.append(".max(");
builder.append(field.sizeMax());
builder.append(")");
}
}
if (clazz == short.class || clazz == Short.class || clazz == int.class || clazz == Integer.class
|| clazz == long.class || clazz == Long.class || clazz == float.class || clazz == Float.class
|| clazz == double.class || clazz == Double.class) {
if (field.min() != null && field.min() > 0) {
builder.append(".min(");
builder.append(field.min());
builder.append(")");
}
if (field.max() != null && field.max() > 0) {
builder.append(".max(");
builder.append(field.max());
builder.append(")");
}
}
return builder.toString();
}
public String readOnlyZod(final FieldProperty field) {
if (field.readOnly()) {
return ".readonly()";
}
return "";
}
public String generateBaseObject() {
final StringBuilder out = new StringBuilder();
out.append(getBaseHeader());
out.append("\n");
out.append("export const ");
out.append(this.zodName);
out.append(" = ");
out.append(this.declaration);
out.append(";");
out.append(generateZodInfer(this.tsTypeName, this.zodName));
return out.toString();
}
public String generateObject(final ClassObjectModel model, final TsClassElementGroup tsGroup) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(getBaseHeader());
out.append(generateImports(model.getDependencyModels(), tsGroup));
out.append("\n");
// ------------------------------------------------------------------------
// -- Generate read mode
// ------------------------------------------------------------------------
out.append(generateComment(model));
out.append("export const ");
out.append(this.zodName);
out.append(" = ");
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append(".extend({");
} else {
out.append("zod.object({");
}
out.append("\n");
for (final FieldProperty field : model.getFields()) {
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(readOnlyZod(field));
out.append(optionalTypeZod(field));
out.append(",\n");
}
final List<String> omitField = model.getReadOnlyField();
out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName, this.zodName));
out.append(generateExportCheckFunctionWrite(""));
// check if we need to generate write mode :
if (!model.isNoWriteSpecificMode()) {
// ------------------------------------------------------------------------
// -- Generate write mode
// ------------------------------------------------------------------------
//out.append(generateComment(model));
out.append("export const ");
out.append(this.zodName);
out.append("Write = ");
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append("Write");
out.append(".extend({");
} else {
out.append("zod.object({");
}
out.append("\n");
for (final FieldProperty field : model.getFields()) {
// remove all readOnly field
if (field.readOnly()) {
continue;
}
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(optionalWriteTypeZod(field));
// all write field are optional
if (field.model() instanceof final ClassObjectModel plop) {
if (!plop.isPrimitive()) {
out.append(".optional()");
}
} else {
out.append(".optional()");
}
out.append(",\n");
}
out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName + "Write", this.zodName + "Write"));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionWrite("Write"));
// Generate the Write Type associated.
/*
out.append("\nexport const ");
out.append(this.zodName);
out.append("Write = ");
out.append(this.zodName);
if (omitField.size() != 0) {
out.append(".omit({\n");
for (final String elem : omitField) {
out.append("\t");
out.append(elem);
out.append(": true,\n");
}
out.append("\n})");
}
out.append(".partial();\n");
*/
}
return out.toString();
}
private static String generateZodInfer(final String tsName, final String zodName) {
final StringBuilder out = new StringBuilder();
out.append("\nexport type ");
out.append(tsName);
out.append(" = zod.infer<typeof ");
out.append(zodName);
out.append(">;\n");
return out.toString();
}
private static String generateTsMap(final ClassMapModel model, final TsClassElementGroup tsGroup) {
final StringBuilder out = new StringBuilder();
out.append("zod.record(");
if (model.keyModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateTsList(fieldListModel, tsGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateTsMap(fieldMapModel, tsGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateTsObject(fieldObjectModel, tsGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateTsEnum(fieldEnumModel, tsGroup);
out.append(tmp);
}
out.append(", ");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateTsList(fieldListModel, tsGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateTsMap(fieldMapModel, tsGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateTsObject(fieldObjectModel, tsGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateTsEnum(fieldEnumModel, tsGroup);
out.append(tmp);
}
out.append(")");
return out.toString();
}
private static String generateTsEnum(final ClassEnumModel model, final TsClassElementGroup tsGroup) {
final TsClassElement tsParentModel = tsGroup.find(model);
return tsParentModel.zodName;
}
private static String generateTsObject(final ClassObjectModel model, final TsClassElementGroup tsGroup) {
final TsClassElement tsParentModel = tsGroup.find(model);
return tsParentModel.zodName;
}
private static String generateTsList(final ClassListModel model, final TsClassElementGroup tsGroup) {
final StringBuilder out = new StringBuilder();
out.append("zod.array(");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateTsList(fieldListModel, tsGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateTsMap(fieldMapModel, tsGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateTsObject(fieldObjectModel, tsGroup);
out.append(tmp);
}
out.append(")");
return out.toString();
}
public void generateFile(final String pathPackage, final TsClassElementGroup tsGroup) throws IOException {
if (this.nativeType == DefinedPosition.NATIVE) {
return;
}
final ClassModel model = this.models.get(0);
String data = "";
if (this.nativeType == DefinedPosition.BASIC && model instanceof ClassObjectModel) {
data = generateBaseObject();
} else if (model instanceof final ClassEnumModel modelEnum) {
data = generateEnum(modelEnum, tsGroup);
} else if (model instanceof final ClassObjectModel modelObject) {
data = generateObject(modelObject, tsGroup);
}
final Path path = Paths.get(pathPackage + File.separator + "model");
if (Files.notExists(path)) {
Files.createDirectories(path);
}
final FileWriter myWriter = new FileWriter(
pathPackage + File.separator + "model" + File.separator + this.fileName + ".ts");
myWriter.write(data);
myWriter.close();
}
private static String generateLocalModelBase(final ClassModel model, final TsClassElementGroup tsGroup)
throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateTsObject(objectModel, tsGroup);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateTsEnum(enumModel, tsGroup);
}
if (model instanceof final ClassListModel listModel) {
return generateTsList(listModel, tsGroup);
}
if (model instanceof final ClassMapModel mapModel) {
return generateTsMap(mapModel, tsGroup);
}
return "";
}
public static String generateLocalModel(
final String ModelName,
final List<ClassModel> models,
final TsClassElementGroup tsGroup) throws IOException {
if (models.size() == 1) {
if (models.get(0) instanceof ClassObjectModel) {
return null;
}
if (models.get(0) instanceof ClassEnumModel) {
return null;
}
}
final StringBuilder out = new StringBuilder();
out.append("export const Zod");
out.append(ModelName);
out.append(" = ");
if (models.size() == 1) {
out.append(generateLocalModelBase(models.get(0), tsGroup));
out.append(";");
} else {
out.append("z.union([\n");
for (final ClassModel model : models) {
out.append("\t");
out.append(generateLocalModelBase(models.get(0), tsGroup));
out.append(",\n");
}
out.append("]);");
}
//model.getDependencyModels()
out.append(generateZodInfer(ModelName, "Zod" + ModelName));
out.append(generateExportCheckFunction("is" + ModelName, ModelName, "Zod" + ModelName));
return out.toString();
}
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.externalRestApi.typescript;
import java.util.List;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class TsClassElementGroup {
private final List<TsClassElement> tsElements;
public List<TsClassElement> getTsElements() {
return this.tsElements;
}
public TsClassElementGroup(final List<TsClassElement> tsElements) {
this.tsElements = tsElements;
}
public TsClassElement find(final ClassModel model) {
for (final TsClassElement elem : this.tsElements) {
if (elem.isCompatible(model)) {
return elem;
}
}
return null;
}
}

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