Compare commits

...

133 Commits

Author SHA1 Message Date
4fc5f68813 [DEV] full rework of Typescript generator (operational) 2024-05-21 21:52:39 +02:00
a7220e0f76 [DEV] first step to generation of API 2024-05-21 00:41:53 +02:00
d28c31290f [FEAT] model generation is ready 2024-05-21 00:09:25 +02:00
be1e189d60 [FEAT] continue refacto of the model generator (base model generated, not the API, mission some control like optional, and limit size 2024-05-20 01:19:25 +02:00
be1bb9c52a [FEAT] continue refacto of the generation code 2024-05-18 08:27:47 +02:00
b64825eaba [FEAT] continue integration 2024-05-17 19:17:34 +02:00
592243ddc6 [DEV] refacto the external rest api generator 2024-05-17 00:41:40 +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
d94e526bd4 [RELEASE] release v0.8.2 2024-04-28 10:32:12 +02:00
b9b008b103 [API] set old add-on deprecated 2024-04-28 10:30:26 +02:00
c43dfa097a [FEAT] add dependency of the primary key and rework quer(r)y 2024-04-28 10:27:30 +02:00
4fd13ceb1d change id to uuid to well understand the id type 2024-04-27 08:53:30 +02:00
6c8ea9e46e [FIX] correct the uuid list getted 2024-04-27 08:52:55 +02:00
e183ea7ff6 [RELEASE] version v0.8.1 2024-04-26 00:03:33 +02:00
69006ca067 [FIX] correct remote element 2024-04-26 00:02:42 +02:00
cfe488f2be [DEV] fix format 2024-04-25 08:28:30 +02:00
cb0ee69820 [RELEASE] create version v0.8.0 2024-04-24 15:48:48 +02:00
cfc7ec2ff1 [FIX] correct the typescript gneration 2024-04-22 23:02:52 +02:00
0605ef0824 [DEV] better management of UUID as remove elemnt 2024-04-20 21:58:31 +02:00
3309abe053 [DEV] update some zond generation file 2024-04-19 08:47:24 +02:00
f069ce7cc3 [DEV] format code 2024-04-17 23:11:04 +02:00
a82c3e725d [RELEASE] create a new version 2024-04-17 23:08:33 +02:00
08124f212a [DEV] update REST tool for TS 2024-04-17 23:08:06 +02:00
631999e135 [FIX] correct the many to One insertion for type other than Long 2024-04-17 12:13:26 +02:00
7674d9a299 [DEV] well support of the callback for stream 2024-04-17 12:12:50 +02:00
9cd71ab601 [DEV] add progress callback ==> does not work 2024-04-15 00:56:00 +02:00
4cb8c84312 [VERSION] create version 0.7.2 2024-03-29 22:37:54 +01:00
5acdbfb0c7 [DEV] update models 2024-03-29 22:37:54 +01:00
d35c83fcbf [DEV] generate a definable UUID 2024-03-19 23:14:11 +01:00
0fe769a203 [FORMAT] error while formating 2024-03-19 08:48:24 +01:00
dbe1b469f6 [DEV] update dev tag version 2024-03-19 08:43:14 +01:00
875b4b313b [RELEASE] new version 0.7.0 2024-03-19 08:42:29 +01:00
15a7814a3b [DEV] update new model 2024-03-19 08:37:07 +01:00
530d254bf2 Many correction of the cover, data ... 2024-03-17 23:43:56 +01:00
e1d9da70c8 [DEV] better zod export 2024-03-15 07:57:13 +01:00
c13f910620 [DEV] better management of generation of multiple Type requested 2024-03-11 22:51:04 +01:00
6944331761 [DEV] add capability to copy the tool file 2024-03-11 22:51:04 +01:00
151aa498e5 [DEV] manage native enum instead of String enum mode 2024-03-11 22:51:04 +01:00
db6a20fa02 [DEV] add mulktipart management 2024-03-11 22:51:04 +01:00
8e913b54ad [DEV] update capacity to upload file 2024-03-11 22:51:04 +01:00
092bef5a7b [DEV] add missing tools for TS 2024-03-11 00:02:35 +01:00
e5d75bc97b Simplify the model 2024-03-10 23:58:44 +01:00
7c217716c9 [DEV] update Typescript API generation 2024-03-10 11:09:52 +01:00
526f902eae [DEV] add API generation (base) 2024-03-09 08:42:26 +01:00
42ac2f3056 [DEV] add capacity to manage isXXX on Zod element 2024-03-04 23:50:44 +01:00
389e4138c3 [DEV] continue migration of the Zood and UUID for Data 2024-03-03 08:49:20 +01:00
6584022861 [DEV] correct timing 2024-03-02 15:21:03 +01:00
41fb181545 [DEV] work on uuid and callbak migration 2024-02-27 08:00:51 +01:00
56609e4f59 [DEV] add management of UUID (not tested) 2024-02-26 19:37:51 +01:00
e0b7ed1e1e [DEV] update dev tag version 2024-02-25 23:57:01 +01:00
48c32a3744 [RELEASE] new version 0.6.3 2024-02-25 23:56:41 +01:00
fc521d2287 [DEV] correct some retreave data from json 2024-02-25 23:55:29 +01:00
15125e2d6d [DEV] add condition in row request and better name naming in condition 2024-02-25 19:11:54 +01:00
6f1dd991c5 [DEV] update dev tag version 2024-02-25 19:10:51 +01:00
009831f5db [RELEASE] new version 0.6.2 2024-02-25 19:10:19 +01:00
1bcdb1df8c [DEBUG] fix the UserDBAccess 2024-02-03 18:47:30 +01:00
7ae948bb79 [DEV] add raw query 2024-02-03 18:47:11 +01:00
29402fc27e [DEV] add csv serializator for user export 2024-02-03 18:46:30 +01:00
eca28292d5 [DEV] start retreive from random request 2024-01-29 01:06:24 +01:00
aef4cdabc3 [DEV] configure mode to access on multiple database 2024-01-28 23:49:12 +01:00
04114aa0cf [DEV] format code 2024-01-19 21:23:29 +01:00
575102fbfa [DEV] update dev tag version 2024-01-19 21:21:43 +01:00
19fa13849e [RELEASE] new version 0.6.1 2024-01-19 21:20:57 +01:00
9ffebf1d1d [DEV] correct the get of list in row 2024-01-19 21:17:29 +01:00
e35a1ae879 [DEV] correct the filter of deleted elements 2024-01-19 21:16:52 +01:00
9c9cf85f92 [DEV] add tool to update version in the pom.xml 2024-01-19 23:00:22 +01:00
1a2302f548 [DEBUG] missing remove a last element of Zota 2024-01-18 23:40:33 +01:00
808784889b [DEV] add email properties 2024-01-17 20:19:10 +01:00
f394254f38 [DEV] select Bearer to be more standard 2024-01-15 23:56:25 +01:00
0574d5da82 [DEV] update dev tag version 2024-01-06 21:25:55 +01:00
62435cd97e [RELEASE] new version 0.6.0 2024-01-06 21:24:21 +01:00
d983210a10 [DE] update rteadme and .gitignore 2024-01-06 21:03:03 +01:00
a704008f7d [DEV] format pom.xml 2024-01-06 09:07:03 +01:00
04a2014799 Update LICENSE 2024-01-05 23:57:34 +01:00
b74b404e72 wrong position of dependabot 2024-01-05 23:56:15 +01:00
42245e026b Create dependabot.yml 2024-01-05 23:55:37 +01:00
284c00a88c [CI] update build path to generic path 2024-01-05 23:52:20 +01:00
0ae8f9ad16 [DEV] remove codacity 2024-01-05 15:28:41 +01:00
7b31c66ce5 [DEV] correct delete API for test 2024-01-05 15:27:55 +01:00
fe63c6b954 try to find a correct maven plugin for dependecy
Signed-off-by: Edouard DUPIN <yui.heero@gmail.com>
2024-01-05 00:11:09 +01:00
b4b6e40250 [DEV] update generation of dependency 2024-01-04 23:51:48 +01:00
4d1f70a637 [DEV] add dependabot 2024-01-04 23:51:44 +01:00
86dc0351fe Update maven.yml 2024-01-04 23:37:49 +01:00
a049d3ca30 Create maven.yml 2024-01-04 23:22:50 +01:00
c90af0bdc9 Create codacy.yml 2024-01-04 23:21:50 +01:00
f3ea7983df [DEV] add clean all for test mode 2024-01-04 21:45:52 +01:00
ba314a97ed [DEV] reduce step time between migration 2024-01-03 00:18:43 +01:00
c99fb84ef4 [DEV] add list at insert 2024-01-03 00:18:20 +01:00
eff5513705 [DEV] Add drop table to improve testing 2024-01-02 12:05:44 +01:00
66796d9591 [DEV] try to fix the ManyToMany link table insertion and update to simplify API 2023-12-31 09:34:46 +01:00
554e2493aa [DEBUG] correct link table (remove the deleted field) 2023-12-31 09:34:10 +01:00
49654698a6 [DEV] remove the timeout control in test mode to permit @swagger to have generic token 2023-12-31 09:32:40 +01:00
61f3e70835 update REST API caller 2023-12-31 09:31:56 +01:00
6ac2f1dcfc [DEV] update comment of swagger 2023-12-31 09:31:34 +01:00
5b7fdce349 [DEV] add specific jackson catcher 2023-12-31 09:29:34 +01:00
058fb2e640 [DEV] fix size error in string 2023-12-31 09:28:42 +01:00
dfc7c77f47 [DEV] update with openAPI 2023-12-27 11:08:18 +01:00
1c82fb1a86 [DEV] upgrade library start to be really cool 2023-12-22 23:34:22 +01:00
3b0c73bd55 [DEV] continue normalisation API 2023-12-18 07:32:46 +01:00
d9fcacc812 [DEV] rework to finalize API 2023-12-16 11:09:53 +01:00
5fc45a23d3 [DEV] add a simple JPA checker to simplify client code 2023-12-10 23:58:38 +01:00
81b6438a7c [DEV] correct the test mode 2023-12-10 23:25:02 +01:00
26ba20d964 [DEV] continue integrations 2023-12-08 20:15:20 +01:00
659f9ca306 [FIX] test unit and style 2023-12-08 20:15:20 +01:00
9f337db62c [DEV] update dev tag version 2023-11-29 19:15:02 +01:00
158 changed files with 10861 additions and 1192 deletions

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry including="**/*.java" kind="src" output="out/maven/classes" path="src">
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="out/maven/test-classes" path="test/src">
<classpathentry kind="src" output="target/test-classes" path="test/src">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
@@ -18,7 +18,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="out/maven/test-classes" path="test/resources">
<classpathentry excluding="**" kind="src" output="target/test-classes" path="test/resources">
<attributes>
<attribute name="test" value="true"/>
<attribute name="maven.pomderived" value="true"/>
@@ -30,5 +30,16 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="out/maven/classes"/>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

37
.github/workflows/maven.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Java CI with Maven
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: find the element
run: find . -name "dependency-graph.json"
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
out/
target/
*.class
.settings/

4
.island/release.bash Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
mvn versions:set -DnewVersion=$(cat version.txt)

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>

375
LICENSE
View File

@@ -1,6 +1,373 @@
PROPIETARY licence
==================
Mozilla Public License Version 2.0
==================================
Copyright at Edouard DUPIN
1. Definitions
--------------
you have no right
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,18 +1,60 @@
Generic backend for archidata in java
===================================
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
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
```
// create a single package jar
mvn clean compile assembly:single
Run the test
```bash
mvn test
```
Install it for external use
```bash
mvn install
```
Develop With Eclipse:
--------------------
generic interface for all KAR web application
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`
Somes tools:
@@ -21,7 +63,7 @@ Somes tools:
Auto-update dependency:
-----------------------
auto-update to the last version dependency:
Auto-update to the last version dependency:
```bash
mvn versions:use-latest-versions
@@ -36,6 +78,22 @@ Simply run the cmd-line:
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:
=============================================
@@ -65,8 +123,8 @@ edit file: ```~/.m2/settings.xml```
release:
========
```
export PATH=/usr/lib/jvm/java-19-openjdk/bin:$PATH
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
mvn install
mvn deploy
```

1
dependabot.yml Normal file
View File

@@ -0,0 +1 @@

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
=========================

145
pom.xml
View File

@@ -3,19 +3,17 @@
<modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.5.0</version>
<version>0.8.10-SNAPSHOOT</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>
<maven.dependency.version>3.1.1</maven.dependency.version>
<jersey.version>3.1.1</jersey.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
@@ -32,7 +30,6 @@
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencyManagement>
<dependencies>
<dependency>
@@ -44,18 +41,17 @@
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<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 -->
@@ -64,8 +60,8 @@
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
@@ -81,14 +77,14 @@
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
@@ -109,38 +105,55 @@
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M1</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>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version>
</dependency>
<!-- Interface for JWT token -->
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.1</version>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.1</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<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.4.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.40.0.0</version>
</dependency>
<!-- Interface for JWT token -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.39</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0-M1</version>
<version>3.2.0-M2</version>
</dependency>
<!-- Swagger dependencies -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.21</version>
</dependency>
<!--
************************************************************
@@ -150,21 +163,35 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<version>5.11.0-M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0-M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>${basedir}/src/resources</directory>
</resource>
</resources>
<testSourceDirectory>test/src</testSourceDirectory>
<directory>${project.basedir}/out/maven/</directory>
<testResources>
<testResource>
<directory>${basedir}/test/resources</directory>
@@ -185,6 +212,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -194,13 +222,27 @@
</execution>
</executions>
</plugin>
<!-- For dependabot plugin -->
<plugin>
<groupId>org.apache.servicemix.tooling</groupId>
<artifactId>depends-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>generate-depends-file</id>
<goals>
<goal>generate-depends-file</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- junit results -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
@@ -212,7 +254,7 @@
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugin>
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -227,7 +269,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<version>3.3.1</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
@@ -239,7 +281,7 @@
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.12.2</version>
<version>2.23.0</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@@ -278,5 +320,4 @@
</plugin>
</plugins>
</reporting>
</project>

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

@@ -7,7 +7,9 @@ public class GlobalConfiguration {
public static DBConfig dbConfig = null;
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,6 +5,9 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.kar.archidata.dataAccess.DataAccess;
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;
@@ -12,21 +15,24 @@ public class UserDB {
public UserDB() {}
public static User getUsers(final long userId) throws Exception {
return DataAccess.get(User.class, userId);
public static User getUsers(final long userId, QueryOption... option) throws Exception {
return DataAccess.get(User.class, userId, option);
}
public static User getUserOrCreate(final long userId, final String userLogin) 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;
}
createUsersInfoFromOAuth(userId, userLogin);
createUsersInfoFromOAuth(userId, userLogin, option);
return getUsers(userId);
}
private static void createUsersInfoFromOAuth(final long userId, final String login) throws IOException {
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
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')";
try {
final PreparedStatement ps = entry.connection.prepareStatement(query);

View File

@@ -10,20 +10,28 @@ import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
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 {
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);
@@ -45,38 +53,158 @@ public class AnnotationTools {
return tmp;
}
public static String getComment(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class);
public static boolean getSchemaReadOnly(final Field element) throws Exception {
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());
}
return ((Schema) annotation[0]).readOnly();
}
public static String getSchemaExample(final Class<?> element) throws Exception {
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 @DataComment on " + element.getClass().getCanonicalName());
throw new Exception(
"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 {
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());
}
return ((Schema) annotation[0]).description();
}
public static String getSchemaDescription(final Field element) throws Exception {
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());
}
return ((Schema) annotation[0]).description();
}
public static String getComment(final Field element) throws Exception {
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());
}
return ((DataComment) annotation[0]).value();
}
public static String getDefault(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataDefault.class);
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 Exception(
"Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
}
return ((DataDefault) annotation[0]).value();
return ((DefaultValue) annotation[0]).value();
}
public static Integer getLimitSize(final Field element) throws Exception {
public static ManyToOne getManyToOne(final Field element) throws Exception {
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 {
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());
}
return (DataJson) annotation[0];
}
public static Long getConstraintsMax(final Field element) throws Exception {
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());
}
return ((Max) annotation[0]).value();
}
public static Long getConstraintsMin(final Field element) throws Exception {
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());
}
return ((Min) annotation[0]).value();
}
public static int getLimitSize(final Field element) throws Exception {
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 Exception(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
final int length = ((Column) annotation[0]).length();
return length <= 0 ? null : length;
return length <= 0 ? 0 : length;
}
public static Size getConstraintsSize(final Field element) throws Exception {
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());
}
return (Size) annotation[0];
}
public static String getConstraintsPattern(final Field element) throws Exception {
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());
}
return ((Pattern) annotation[0]).regexp();
}
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {
@@ -108,7 +236,8 @@ public class AnnotationTools {
return element.getName();
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new Exception(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
final String name = ((Column) annotation[0]).name();
if (name.isBlank()) {
@@ -117,24 +246,59 @@ public class AnnotationTools {
return name;
}
public static boolean getNotNull(final Field element) throws Exception {
public static boolean getColumnNotNull(final Field element) throws Exception {
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 Exception(
"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 {
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());
}
return true;
}
public static Field getPrimaryKeyField(final Class<?> clazz) throws Exception {
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;
}
if (AnnotationTools.isPrimaryKey(field)) {
return field;
}
}
return null;
}
public static boolean isPrimaryKey(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static boolean isUnique(final Field element) throws Exception {
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 Exception(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
return ((Column) annotation[0]).unique();
}
@@ -145,7 +309,8 @@ public class AnnotationTools {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
throw new Exception(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
return ((GeneratedValue) annotation[0]).strategy();
}
@@ -245,7 +410,8 @@ public class AnnotationTools {
}
public static boolean isGenericField(final Field elem) throws Exception {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem);
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem)
|| AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
}
public static Field getFieldOfId(final Class<?> clazz) throws Exception {
@@ -261,4 +427,17 @@ public class AnnotationTools {
return null;
}
public static Field getFieldNamed(final Class<?> clazz, final String name) throws Exception {
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;
}
if (AnnotationTools.getFieldName(field).equals(name)) {
return field;
}
}
return null;
}
}

View File

@@ -0,0 +1,17 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AsyncType {
// Possible class values.
Class<?>[] value();
// direct copy value in the TypeScript (separate with type by a |
String[] tsComplement() default {};
}

View File

@@ -7,8 +7,7 @@ import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Deprecated(since = "0.5.2")
public @interface DataComment {
String value();
}

View File

@@ -5,6 +5,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface;
import org.kar.archidata.dataAccess.options.CheckFunctionVoid;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataJson {}
public @interface DataJson {
Class<? extends CheckFunctionInterface> checker() default CheckFunctionVoid.class;
Class<?> targetEntity() default Void.class;
}

View File

@@ -5,10 +5,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.FIELD })
/** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataDefault {
String value();
}
public @interface TypeScriptProgress {}

View File

@@ -16,6 +16,7 @@ import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.UUID;
import javax.imageio.ImageIO;
@@ -24,12 +25,14 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
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.filter.GenericContext;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable;
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.GET;
@@ -54,7 +57,7 @@ import jakarta.ws.rs.core.StreamingOutput;
@Path("/data")
@Produces(MediaType.APPLICATION_JSON)
public class DataResource {
private static final Logger LOGGER = LoggerFactory.getLogger(MediaType.class);
private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class);
private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks
/** Upload some datas */
@@ -81,8 +84,9 @@ public class DataResource {
return filePath;
}
public static String getFileData(final long tmpFolderId) {
final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data";
public static String getFileDataOld(final long tmpFolderId) {
final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator
+ "data";
try {
createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator);
} catch (final IOException e) {
@@ -91,10 +95,31 @@ public class DataResource {
return filePath;
}
public static String getFileData(final UUID uuid) {
final String stringUUID = uuid.toString();
final String part1 = stringUUID.substring(0, 2);
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;
try {
createFolder(filePath);
} catch (final IOException e) {
e.printStackTrace();
}
filePath += part3;
return filePath;
}
public static String getFileMetaData(final UUID uuid) {
return getFileData(uuid) + ".json";
}
public static Data getWithSha512(final String sha512) {
LOGGER.info("find sha512 = {}", sha512);
try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -113,19 +138,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;
@@ -135,11 +161,10 @@ public class DataResource {
try {
injectedData = DataAccess.insert(injectedData);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
final String mediaPath = getFileData(injectedData.id);
final String mediaPath = getFileData(injectedData.uuid);
LOGGER.info("src = {}", tmpPath);
LOGGER.info("dst = {}", mediaPath);
Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE);
@@ -147,6 +172,25 @@ public class DataResource {
return injectedData;
}
public static void modeFileOldModelToNewModel(final long id, final UUID uuid) throws IOException {
String mediaCurentPath = getFileDataOld(id);
String mediaDestPath = getFileData(uuid);
LOGGER.info("src = {}", mediaCurentPath);
LOGGER.info("dst = {}", mediaDestPath);
if (Files.exists(Paths.get(mediaCurentPath))) {
LOGGER.info("move: {} ==> {}", mediaCurentPath, mediaDestPath);
Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE);
}
// Move old meta-data...
mediaCurentPath = mediaCurentPath.substring(mediaCurentPath.length() - 4) + "meta.json";
mediaDestPath = mediaCurentPath.substring(mediaDestPath.length() - 4) + "meta.json";
if (Files.exists(Paths.get(mediaCurentPath))) {
LOGGER.info("moveM: {} ==> {}", mediaCurentPath, mediaDestPath);
Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE);
}
LOGGER.info("Move done");
}
public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) {
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
}
@@ -205,7 +249,7 @@ public class DataResource {
return sb.toString();
}
public Data getSmall(final Long id) {
public Data getSmall(final UUID id) {
try {
return DataAccess.get(Data.class, id);
} catch (final Exception e) {
@@ -219,7 +263,11 @@ public class DataResource {
@Path("/upload/")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@RolesAllowed("ADMIN")
public Response uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) {
@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) {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.info("===================================================");
LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken));
@@ -233,48 +281,55 @@ public class DataResource {
e.printStackTrace();
}
saveFile(fileInputStream, filePath);
return Response.ok("Data uploaded successfully !!").build();
// return null;
}
@GET
@Path("{id}")
@Path("{uuid}")
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id) throws Exception {
@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 {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
LOGGER.info("== DATA retriveDataId ? id={} user={}", id, (gc == null ? "null" : gc.userByToken));
LOGGER.info("== DATA retrieveDataId ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken));
// logger.info("===================================================");
final Data value = getSmall(id);
final Data value = getSmall(uuid);
if (value == null) {
Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType);
return buildStream(getFileData(uuid), range, value.mimeType);
}
@GET
@Path("thumbnail/{id}")
@Path("thumbnail/{uuid}")
@RolesAllowed("USER")
@PermitTokenInURI
@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 retriveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id) 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 Exception {
// GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
// logger.info("== DATA retriveDataThumbnailId ? {}", (gc==null?"null":gc.user));
// logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user));
// logger.info("===================================================");
final Data value = getSmall(id);
final Data value = getSmall(uuid);
if (value == null) {
return Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
return Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
final String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data";
final String filePathName = getFileData(uuid);
final File inputFile = new File(filePathName);
if (!inputFile.exists()) {
return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").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")
@@ -282,7 +337,8 @@ public class DataResource {
// reads input image
final BufferedImage inputImage = ImageIO.read(inputFile);
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());
@@ -297,11 +353,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();
@@ -313,23 +371,27 @@ public class DataResource {
return buildStream(filePathName, range, value.mimeType);
}
// @Secured
@GET
@Path("{id}/{name}")
@Path("{uuid}/{name}")
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id, @PathParam("name") final String name) throws Exception {
@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 {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("===================================================");
LOGGER.info("== DATA retriveDataFull ? id={} user={}", id, (gc == null ? "null" : gc.userByToken));
LOGGER.info("== DATA retrieveDataFull ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken));
// logger.info("===================================================");
final Data value = getSmall(id);
final Data value = getSmall(uuid);
if (value == null) {
Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType);
return buildStream(getFileData(uuid), range, value.mimeType);
}
/** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541
@@ -389,8 +451,10 @@ public class DataResource {
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()));
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);
}

View File

@@ -6,6 +6,7 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
@@ -88,6 +89,7 @@ public class FrontGeneric {
@GET
@PermitAll()
@Operation(description = "Retrieve native element (index)", tags = "SYSTEM")
// @Produces(MediaType.APPLICATION_OCTET_STREAM)
// @CacheMaxAge(time = 1, unit = TimeUnit.DAYS)
public Response retrive0() throws Exception {
@@ -97,6 +99,7 @@ public class FrontGeneric {
@GET
@Path("{any: .*}")
@PermitAll()
@Operation(description = "Get specific file from the front environment", tags = "SYSTEM")
// @Produces(MediaType.APPLICATION_OCTET_STREAM)
// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retrive1(@PathParam("any") final List<PathSegment> segments) 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

@@ -0,0 +1,34 @@
package org.kar.archidata.api;
import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.ServletConfig;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
@Path("/openapi")
public class openApiResource extends BaseOpenApiResource {
@Context
ServletConfig config;
@Context
Application app;
@GET
@Path("swagger.json")
@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 {
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.getMessage());
}
}

View File

@@ -15,11 +15,13 @@ public class FailException404API implements ExceptionMapper<ClientErrorException
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();
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());
return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception",
exception.getMessage());
}
}

View File

@@ -13,6 +13,7 @@ public class FailExceptionCatcher implements ExceptionMapper<FailException> {
@Override
public Response toResponse(final FailException exception) {
LOGGER.warn("Catch FailException:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
// Not display backtrace ==> this may be a normal case ...

View File

@@ -13,14 +13,17 @@ public class InputExceptionCatcher implements ExceptionMapper<InputException> {
@Override
public Response toResponse(final InputException exception) {
LOGGER.warn("Catch InputException:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();
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

@@ -0,0 +1,30 @@
package org.kar.archidata.catcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
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);
@Override
public Response toResponse(final JsonProcessingException 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();
}
private RestErrorResponse build(final Exception exception) {
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch JSON Exception",
exception.getMessage());
}
}

View File

@@ -3,19 +3,29 @@ package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.UUID;
import org.kar.archidata.tools.UuidUtils;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response;
public class RestErrorResponse {
public UUID uuid = UUID.randomUUID();
@NotNull
public UUID uuid = UuidUtils.nextUUID();
@NotNull
public String name; // Mandatory for TS generic error
@NotNull
public String message; // Mandatory for TS generic error
@NotNull
public String time;
public String error;
public String message;
@NotNull
final public int status;
@NotNull
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.error = error;
this.name = error;
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
@@ -23,13 +33,15 @@ public class RestErrorResponse {
public RestErrorResponse(final Response.Status status, final String error, final String message) {
this.time = Instant.now().toString();
this.error = error;
this.name = error;
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
}
public RestErrorResponse(final Response.Status status) {
this.name = "generic";
this.message = "";
this.time = Instant.now().toString();
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();

View File

@@ -13,6 +13,7 @@ public class SystemExceptionCatcher implements ExceptionMapper<SystemException>
@Override
public Response toResponse(final SystemException exception) {
LOGGER.warn("Catch SystemException:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();

File diff suppressed because it is too large Load Diff

View File

@@ -29,19 +29,41 @@ 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
boolean canInsert(final Field field);
/** Element can insert in the single request
* @param field
* @return */
default boolean canInsert(final Field field) {
return false;
}
// Element can be retrieve with the specific mode
boolean canRetrieve(final Field field);
/** Element can be retrieve with the specific mode
* @param field
* @return */
default boolean canRetrieve(final Field field) {
return false;
}
void generateQuerry(@NotNull String tableName, @NotNull Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @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 fillFromQuerry(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.
@@ -53,7 +75,67 @@ public interface DataAccessAddOn {
* @param createDrop
* @param fieldId
* @throws Exception */
void createTables(String tableName, 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
* @return */
default boolean isInsertAsync(final Field field) throws Exception {
return false;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @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 {
}
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
default boolean isUpdateAsync(final Field field) throws Exception {
return false;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @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 drop(final String tableName, final Field field) throws Exception {
}
default void cleanAll(final String tableName, final Field field) throws Exception {
}
}

View File

@@ -0,0 +1,416 @@
package org.kar.archidata.dataAccess;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
public class DataExport {
static final Logger LOGGER = LoggerFactory.getLogger(DataExport.class);
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 {
if (type == UUID.class) {
return (final ResultSet rs, final Object obj) -> {
final UUID tmp = rs.getObject(count, UUID.class);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Long.class) {
return (final ResultSet rs, final Object obj) -> {
final Long tmp = rs.getLong(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == long.class) {
return (final ResultSet rs, final Object obj) -> {
final Long tmp = rs.getLong(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Integer.class) {
return (final ResultSet rs, final Object obj) -> {
final Integer tmp = rs.getInt(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == int.class) {
return (final ResultSet rs, final Object obj) -> {
final Integer tmp = rs.getInt(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Float.class) {
return (final ResultSet rs, final Object obj) -> {
final Float tmp = rs.getFloat(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == float.class) {
return (final ResultSet rs, final Object obj) -> {
final Float tmp = rs.getFloat(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Double.class) {
return (final ResultSet rs, final Object obj) -> {
final Double tmp = rs.getDouble(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == double.class) {
return (final ResultSet rs, final Object obj) -> {
final Double tmp = rs.getDouble(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Boolean.class) {
return (final ResultSet rs, final Object obj) -> {
final Boolean tmp = rs.getBoolean(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == boolean.class) {
return (final ResultSet rs, final Object obj) -> {
final Boolean tmp = rs.getBoolean(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Timestamp.class) {
return (final ResultSet rs, final Object obj) -> {
final Timestamp tmp = rs.getTimestamp(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type == Date.class) {
return (final ResultSet rs, final Object obj) -> {
try {
final Timestamp tmp = rs.getTimestamp(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, Date.from(tmp.toInstant()));
}
} catch (final SQLException ex) {
final String tmp = rs.getString(count);
LOGGER.error("Fail to parse the SQL time !!! {}", tmp);
if (!rs.wasNull()) {
final Date date = DateTools.parseDate(tmp);
LOGGER.error("Fail to parse the SQL time !!! {}", date);
final List<Object> data = (List<Object>) (obj);
data.set(id, date);
}
}
};
}
if (type == Instant.class) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (!rs.wasNull()) {
final Instant date = Instant.parse(tmp);
LOGGER.error("Fail to parse the SQL time !!! {}", date);
final List<Object> data = (List<Object>) (obj);
data.set(id, date);
}
};
}
if (type == LocalDate.class) {
return (final ResultSet rs, final Object obj) -> {
final java.sql.Date tmp = rs.getDate(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp.toLocalDate());
}
};
}
if (type == LocalTime.class) {
return (final ResultSet rs, final Object obj) -> {
final java.sql.Time tmp = rs.getTime(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp.toLocalTime());
}
};
}
if (type == String.class) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (!rs.wasNull()) {
final List<Object> data = (List<Object>) (obj);
data.set(id, tmp);
}
};
}
if (type.isEnum()) {
return (final ResultSet rs, final Object obj) -> {
final String tmp = rs.getString(count);
if (!rs.wasNull()) {
boolean find = false;
final Object[] arr = type.getEnumConstants();
for (final Object elem : arr) {
if (elem.toString().equals(tmp)) {
final List<Object> data = (List<Object>) (obj);
data.set(id, elem);
find = true;
break;
}
}
if (!find) {
throw new DataAccessException("Enum value does not exist in the Model: '" + tmp + "'");
}
}
};
}
throw new DataAccessException("Unknown Field Type");
}
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;
}
}
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 {
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 {
final List<LazyGetter> lazyCall = new ArrayList<>();
// TODO ... final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
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 List<GroupBy> groups = options.get(GroupBy.class);
for (final GroupBy group : groups) {
group.generateQuery(query, null);
}
final List<OrderBy> orders = options.get(OrderBy.class);
for (final OrderBy order : orders) {
order.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 CountInOut iii = new CountInOut(1);
if (parameters != null) {
for (final Object elem : parameters) {
DataAccess.addElement(ps, elem, iii);
}
iii.inc();
}
condition.injectQuery(ps, iii);
if (limits.size() == 1) {
limits.get(0).injectQuery(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
final ResultSetMetaData rsmd = rs.getMetaData();
final List<RetreiveFromDB> actionToRetreive = new ArrayList<>();
LOGGER.info("Field:");
for (int jjj = 0; jjj < rsmd.getColumnCount(); jjj++) {
final String label = rsmd.getColumnLabel(jjj + 1);
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());
// create the callback...
final RetreiveFromDB element = createSetValueFromDbCallbackTable(jjj + 1, headers.get(id).type, id);
actionToRetreive.add(element);
}
while (rs.next()) {
count.value = 1;
final List<Object> data = Arrays.asList(new Object[headers.size()]);
for (final RetreiveFromDB action : actionToRetreive) {
action.doRequest(rs, data);
}
out.values.add(data);
}
LOGGER.info("Async calls: {}", lazyCall.size());
for (final LazyGetter elem : lazyCall) {
elem.doRequest();
}
} catch (final SQLException ex) {
ex.printStackTrace();
throw ex;
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
entry.close();
}
return out;
}
public static String CSVReplaceSeparator(final String data, final String separator) {
if (data == null) {
return "";
}
final String separatorLine = "\n";
final String separatorLineReplace = "\\n";
return data.replaceAll(separator, "__SEP__").replaceAll(separatorLine, separatorLineReplace);
}
public static String tableToCSV(final TableQuery data) {
final String separator = ";";
final StringBuilder out = new StringBuilder();
// generate header:
boolean first = true;
for (final TableQueryTypes elem : data.headers) {
if (!first) {
out.append(separator);
} else {
first = false;
}
out.append(CSVReplaceSeparator(elem.title, separator));
}
out.append("\n");
// generate body:
first = true;
for (final List<Object> line : data.values) {
for (final Object elem : line) {
if (!first) {
out.append(separator);
} else {
first = false;
}
if (elem != null) {
out.append(CSVReplaceSeparator(elem.toString(), separator));
}
}
out.append("\n");
}
return out.toString();
}
public static Response convertInResponse(final TableQuery dataOut, final String accept)
throws DataAccessException, IOException {
if (CSV_TYPE.equals(accept)) {
// CSV serialization
String out = null;
try {
out = DataExport.tableToCSV(dataOut);
} catch (final Exception e) {
LOGGER.error("Fail to generate CSV....");
e.printStackTrace();
throw new DataAccessException("Fail in CSV convertion data");
}
return Response.ok(out).header("Content-Type", CSV_TYPE).build();
}
if (MediaType.APPLICATION_JSON.equals(accept)) {
LOGGER.info("Start mapping josn");
final ObjectMapper objectMapper = new ObjectMapper();
LOGGER.info("Start find modules josn");
objectMapper.findAndRegisterModules();
LOGGER.info("Start map object");
String out;
try {
out = objectMapper.writeValueAsString(dataOut);
} catch (final JsonProcessingException e) {
LOGGER.error("Fail to generate JSON....");
e.printStackTrace();
throw new DataAccessException("Fail in JSON convertion data");
}
LOGGER.info("generate response");
return Response.ok(out).header("Content-Type", MediaType.APPLICATION_JSON).build();
}
throw new IOException("This type is not managed: '" + accept + "'");
}
}

View File

@@ -2,11 +2,13 @@ package org.kar.archidata.dataAccess;
import java.lang.reflect.Field;
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.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.CreationTimestamp;
@@ -27,6 +29,9 @@ public class DataFactory {
public static String convertTypeInSQL(final Class<?> type, final String fieldName) throws Exception {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
if (type == UUID.class) {
return "binary(16)";
}
if (type == Long.class || type == long.class) {
return "bigint";
}
@@ -42,6 +47,9 @@ public class DataFactory {
if (type == Double.class || type == double.class) {
return "double";
}
if (type == Instant.class) {
return "varchar(33)";
}
if (type == Date.class || type == Timestamp.class) {
return "timestamp(3)";
}
@@ -75,6 +83,9 @@ public class DataFactory {
return out.toString();
}
} else {
if (type == UUID.class) {
return "BINARY(16)";
}
if (type == Long.class || type == long.class) {
return "INTEGER";
}
@@ -90,6 +101,9 @@ public class DataFactory {
if (type == Double.class || type == double.class) {
return "REAL";
}
if (type == Instant.class) {
return "text";
}
if (type == Date.class || type == Timestamp.class) {
return "DATETIME";
}
@@ -128,11 +142,20 @@ 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 Integer limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getNotNull(elem);
final int limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getColumnNotNull(elem);
final boolean primaryKey = AnnotationTools.isPrimaryKey(elem);
final GenerationType strategy = AnnotationTools.getStrategy(elem);
@@ -152,7 +175,7 @@ public class DataFactory {
String typeValue = null;
typeValue = convertTypeInSQL(classModel, name);
if ("text".equals(typeValue) && !"sqlite".equals(ConfigBaseVariable.getDBType())) {
if (limitSize != null) {
if (limitSize > 0) {
mainTableBuilder.append("varchar(");
mainTableBuilder.append(limitSize);
mainTableBuilder.append(")");
@@ -172,16 +195,24 @@ public class DataFactory {
}
if (defaultValue == null) {
if (updateTime || createTime) {
mainTableBuilder.append("DEFAULT CURRENT_TIMESTAMP");
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("(3)");
if ("varchar(33)".equals(typeValue)) {
mainTableBuilder.append("DEFAULT DATE_FORMAT(now(6), '%Y-%m-%dT%H:%m:%s.%fZ')");
} else {
mainTableBuilder.append("DEFAULT CURRENT_TIMESTAMP");
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("(3)");
}
}
mainTableBuilder.append(" ");
}
if (updateTime) {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("ON UPDATE CURRENT_TIMESTAMP");
mainTableBuilder.append("(3)");
if ("varchar(33)".equals(typeValue)) {
mainTableBuilder.append("ON UPDATE DATE_FORMAT(now(6), '%Y-%m-%dT%H:%m:%s.%fZ')");
} else {
mainTableBuilder.append("ON UPDATE CURRENT_TIMESTAMP");
mainTableBuilder.append("(3)");
}
} else {
// TODO: add trigger:
/* CREATE TRIGGER your_table_trig AFTER UPDATE ON your_table BEGIN update your_table SET updated_on = datetime('now') WHERE user_id = NEW.user_id; END; */
@@ -195,7 +226,14 @@ public class DataFactory {
triggerBuilder.append(" SET ");
triggerBuilder.append(name);
// triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n");
triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', '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 " + tablePrimaryKey
+ " = NEW." + tablePrimaryKey + "; \n");
} else {
triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', 'now') WHERE " + tablePrimaryKey
+ " = NEW." + tablePrimaryKey + "; \n");
}
triggerBuilder.append("END;");
postOtherTables.add(triggerBuilder.toString());
@@ -241,8 +279,11 @@ public class DataFactory {
mainTableBuilder.append("PRIMARY KEY ");
}
if (strategy == GenerationType.IDENTITY) {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
if ("binary(16)".equals(typeValue)) {
} else if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
mainTableBuilder.append("AUTO_INCREMENT ");
} else {
mainTableBuilder.append("AUTOINCREMENT ");
@@ -310,6 +351,7 @@ public class DataFactory {
LOGGER.debug("===> TABLE `{}`", tableName);
final List<String> primaryKeys = new ArrayList<>();
final Field primaryField = AnnotationTools.getPrimaryKeyField(clazz);
for (final Field elem : clazz.getFields()) {
// DEtect the primary key (support only one primary key right now...
if (AnnotationTools.isPrimaryKey(elem)) {
@@ -321,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());
@@ -342,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, 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

@@ -0,0 +1,524 @@
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.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement;
import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes;
import org.kar.archidata.externalRestApi.model.ApiTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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);
final List<String> includedElements = new ArrayList<>();
for (final Class<?> elem : includeModel) {
if (elem == null) {
continue;
}
final ClassElement classElement = DataFactoryZod.createTable(elem, previous);
if (classElement.nativeType) {
continue;
}
includedElements.add(classElement.tsTypeName);
}
Collections.sort(includedElements);
for (final String elem : includedElements) {
generatedData.append("\n ");
generatedData.append(elem);
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;
}
record OrderedElement(
String methodName,
Method method) {}
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 = ApiTool.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);
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) {
final Method method = orderedElement.method();
final String methodName = orderedElement.methodName();
final String methodPath = ApiTool.apiAnnotationGetPath(method);
final String methodType = ApiTool.apiAnnotationGetTypeRequest(method);
if (methodType == null) {
LOGGER.error(" [{}] {} => {}/{} ==> No methode type @PATH, @GET ...", methodType, methodName,
basicPath, methodPath);
continue;
}
final String methodDescription = ApiTool.apiAnnotationGetOperationDescription(method);
final List<String> consumes = ApiTool.apiAnnotationGetConsumes(clazz, method);
List<String> produces = ApiTool.apiAnnotationProduces(clazz, method);
LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath);
if (methodDescription != null) {
LOGGER.trace(" description: {}", methodDescription);
}
final boolean needGenerateProgress = ApiTool.apiAnnotationTypeScriptProgress(method);
Class<?>[] returnTypeModel = ApiTool.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 || returnTypeModelRaw == void.class
|| returnTypeModelRaw == Void.class) {
if (returnTypeModelRaw == Response.class) {
isUnmanagedReturnType = true;
}
returnTypeModel = new Class<?>[] { Void.class };
tmpReturn = new ArrayList<>();
produces = null;
} else if (returnTypeModelRaw == Map.class) {
LOGGER.warn("Not manage the Map Model ... set any");
returnTypeModel = new Class<?>[] { Map.class };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
} 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 if (returnTypeModel.length >= 0 && (returnTypeModel[0] == Response.class
|| returnTypeModel[0] == Void.class || returnTypeModel[0] == void.class)) {
if (returnTypeModel[0] == Response.class) {
isUnmanagedReturnType = true;
}
returnTypeModel = new Class<?>[] { Void.class };
tmpReturn = new ArrayList<>();
produces = null;
} else if (returnTypeModel.length > 0 && returnTypeModel[0] == Map.class) {
LOGGER.warn("Not manage the Map Model ...");
returnTypeModel = new Class<?>[] { Map.class };
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 (ApiTool.apiAnnotationIsContext(parameter)) {
continue;
}
final Class<?> parameterType = parameter.getType();
String parameterTypeString;
final Class<?>[] asyncType = ApiTool.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 = ApiTool.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 = ApiTool.convertInTypeScriptType(tmp, true);
}
final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = ApiTool.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 != null && 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 != null && 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(ApiTool.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(ApiTool.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

@@ -0,0 +1,471 @@
package org.kar.archidata.dataAccess;
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.Map;
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));
// Map is binded to any ==> can not determine this complex model for now
previous.add(new ClassElement(new Class<?>[] { Map.class }, "any", "any", 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.string().datetime({ precision: 3 })", false), true);
previous.add(new ClassElement(new Class<?>[] { Timestamp.class }, "ZodTimestamp", "Timestamp", "isTimestamp",
"zod.string().datetime({ precision: 3 })", false), true);
previous.add(new ClassElement(new Class<?>[] { LocalDate.class }, "ZodLocalDate", "LocalDate", "isLocalDate",
"zod.string().date()", false), true);
previous.add(new ClassElement(new Class<?>[] { LocalTime.class }, "ZodLocalTime", "LocalTime", "isLocalTime",
"zod.string().time()", 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;
}
if (clazz.isEnum()) {
return convertTypeZodEnum(clazz, previous);
}
// 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();
}
}

View File

@@ -8,39 +8,47 @@ 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... child) {
Collections.addAll(this.childs, child);
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
public void generateQuery(final StringBuilder query, final String tableName) {
if (this.childs.size() >= 1) {
querry.append(" (");
query.append(" (");
}
boolean first = true;
for (final QueryItem elem : this.childs) {
if (first) {
first = false;
} else {
querry.append(" AND ");
query.append(" AND ");
}
elem.generateQuerry(querry, tableName);
elem.generateQuery(query, tableName);
}
if (this.childs.size() >= 1) {
querry.append(")");
query.append(")");
}
}
@Override
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
for (final QueryItem elem : this.childs) {
elem.injectQuerry(ps, iii);
elem.injectQuery(ps, iii);
}
}
public int size() {
return this.childs.size();
}
}

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;
@@ -14,18 +20,19 @@ public class QueryCondition implements QueryItem {
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
querry.append(tableName);
querry.append(".");
querry.append(this.key);
querry.append(" ");
querry.append(this.comparator);
querry.append(" ?");
public void generateQuery(final StringBuilder query, final String tableName) {
if (tableName != null) {
query.append(tableName);
query.append(".");
}
query.append(this.key);
query.append(" ");
query.append(this.comparator);
query.append(" ?");
}
@Override
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
DataAccess.addElement(ps, this.value, iii);
iii.inc();
}

View File

@@ -18,27 +18,33 @@ public class QueryInList<T> implements QueryItem {
this(key, "IN", value);
}
public QueryInList(final String key, final T... value) {
this(key, "IN", List.of(value));
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
querry.append(tableName);
querry.append(".");
querry.append(this.key);
querry.append(" ");
querry.append(this.comparator);
querry.append(" (");
public void generateQuery(final StringBuilder query, final String tableName) {
if (tableName != null) {
query.append(tableName);
query.append(".");
}
query.append(this.key);
query.append(" ");
query.append(this.comparator);
query.append(" (");
for (int iii = 0; iii < this.value.size(); iii++) {
if (iii != 0) {
querry.append(",?");
query.append(",?");
} else {
querry.append("?");
query.append("?");
}
}
querry.append(")");
query.append(")");
}
@Override
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
for (final Object elem : this.value) {
DataAccess.addElement(ps, elem, iii);
iii.inc();

View File

@@ -3,7 +3,7 @@ package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
public interface QueryItem {
void generateQuerry(StringBuilder querry, String tableName);
void generateQuery(StringBuilder query, String tableName);
void injectQuerry(PreparedStatement ps, CountInOut iii) throws Exception;
void injectQuery(PreparedStatement ps, CountInOut iii) throws Exception;
}

View File

@@ -0,0 +1,24 @@
package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
public class QueryNotNull implements QueryItem {
private final String key;
public QueryNotNull(final String key) {
this.key = key;
}
@Override
public void generateQuery(final StringBuilder query, final String tableName) {
if (tableName != null) {
query.append(tableName);
query.append(".");
}
query.append(this.key);
query.append(" IS NOT NULL");
}
@Override
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {}
}

View File

@@ -0,0 +1,24 @@
package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
public class QueryNull implements QueryItem {
private final String key;
public QueryNull(final String key) {
this.key = key;
}
@Override
public void generateQuery(final StringBuilder query, final String tableName) {
if (tableName != null) {
query.append(tableName);
query.append(".");
}
query.append(this.key);
query.append(" IS NULL");
}
@Override
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {}
}

View File

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

View File

@@ -6,6 +6,7 @@ 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;
@@ -18,12 +19,15 @@ public class QueryOptions {
private final List<QueryOption> options = new ArrayList<>();
public QueryOptions() {}
public QueryOptions(final QueryOption... elems) {
if (elems == null || elems.length == 0) {
return;
}
Collections.addAll(this.options, elems);
}
public QueryOptions() {}
public void add(final QueryOption option) {
this.options.add(option);
}
@@ -32,14 +36,19 @@ public class QueryOptions {
return this.options;
}
public QueryOption[] getAllArray() {
return this.options.toArray(new QueryOption[0]);
}
@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) {
@@ -57,5 +66,4 @@ public class QueryOptions {
}
return false;
}
}

View File

@@ -10,29 +10,33 @@ public class QueryOr implements QueryItem {
this.childs = childs;
}
public QueryOr(final QueryItem... childs) {
this.childs = List.of(childs);
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
public void generateQuery(final StringBuilder query, final String tableName) {
if (this.childs.size() >= 1) {
querry.append(" (");
query.append(" (");
}
boolean first = true;
for (final QueryItem elem : this.childs) {
if (first) {
first = false;
} else {
querry.append(" OR ");
query.append(" OR ");
}
elem.generateQuerry(querry, tableName);
elem.generateQuery(query, tableName);
}
if (this.childs.size() >= 1) {
querry.append(")");
query.append(")");
}
}
@Override
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
for (final QueryItem elem : this.childs) {
elem.injectQuerry(ps, iii);
elem.injectQuery(ps, iii);
}
}
}

View File

@@ -0,0 +1,7 @@
package org.kar.archidata.dataAccess;
import java.sql.ResultSet;
public interface RetreiveFromDB {
void doRequest(final ResultSet rs, Object obj) throws Exception;
}

View File

@@ -1,22 +1,32 @@
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.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
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.kar.archidata.dataAccess.addOn.model.TableCoversLongLong;
import org.kar.archidata.dataAccess.addOn.model.TableCoversLongUUID;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDLong;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDUUID;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.constraints.NotNull;
@@ -42,7 +52,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 Exception {
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
@@ -58,36 +69,252 @@ public class AddOnDataJson implements DataAccessAddOn {
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 generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception {
querrySelect.append(" ");
querrySelect.append(tableName);
querrySelect.append(".");
querrySelect.append(name);
elemCount.inc();
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(".");
querySelect.append(name);
count.inc();
return;
}
@Override
public void fillFromQuerry(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()) {
final ObjectMapper objectMapper = new ObjectMapper();
if (field.getType() == List.class) {
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
if (listClass == Long.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Float.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Float>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Double.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Double>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Integer.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Integer>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Short.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Short>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == String.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == UUID.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName());
}
final Object dataParsed = objectMapper.readValue(jsonData, field.getType());
field.set(data, dataParsed);
}
}
@Override
public void createTables(final String tableName, 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 {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
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 {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
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 {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id,
new OverrideTableName(tableName));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final UUID elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
}
public static void addLink(final Class<?> clazz, final UUID id, 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));
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final Long elem : data.covers) {
if (elem.equals(remoteKey)) {
return;
}
}
data.covers.add(remoteKey);
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 Long remoteKey)
throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.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 UUID 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));
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 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

@@ -7,6 +7,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
@@ -18,7 +19,11 @@ import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryInList;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOn.model.LinkTable;
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongLong;
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongUUID;
import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDLong;
import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDUUID;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
@@ -31,7 +36,8 @@ import jakarta.validation.constraints.NotNull;
public class AddOnManyToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR = "-";
static final String SEPARATOR_LONG = "-";
static final String SEPARATOR_UUID = "_";
@Override
public Class<?> getAnnotationClass() {
@@ -50,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 {
}
@@ -61,7 +68,22 @@ public class AddOnManyToMany implements DataAccessAddOn {
@Override
public boolean canRetrieve(final Field field) {
return true;
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 ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
public static String generateLinkTableNameField(final String tableName, final Field field) throws Exception {
@@ -77,55 +99,81 @@ public class AddOnManyToMany implements DataAccessAddOn {
return tableName + "_link_" + localName;
}
public void generateConcatQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry,
@NotNull final String name, @NotNull final CountInOut elemCount, 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) throws Exception {
final String linkTableName = generateLinkTableName(tableName, name);
final String tmpVariable = "tmp_" + Integer.toString(elemCount.value);
querrySelect.append(" (SELECT GROUP_CONCAT(");
querrySelect.append(tmpVariable);
querrySelect.append(".object2Id ");
if (ConfigBaseVariable.getDBType().equals("sqlite")) {
querrySelect.append(", ");
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);
querySelect.append(".object2Id ");
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querrySelect.append("SEPARATOR ");
querySelect.append("SEPARATOR ");
}
querrySelect.append("'");
querrySelect.append(SEPARATOR);
querrySelect.append("') FROM ");
querrySelect.append(linkTableName);
querrySelect.append(" ");
querrySelect.append(tmpVariable);
querrySelect.append(" WHERE ");
querrySelect.append(tmpVariable);
querrySelect.append(".deleted = false AND ");
querrySelect.append(tableName);
querrySelect.append(".id = ");
querrySelect.append(tmpVariable);
querrySelect.append(".");
querrySelect.append("object1Id ");
if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
querrySelect.append(" GROUP BY ");
querrySelect.append(tmpVariable);
querrySelect.append(".object1Id");
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
} else if (objectClass == UUID.class) {} else {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
}
querrySelect.append(") AS ");
querrySelect.append(name);
querrySelect.append(" ");
querySelect.append("') FROM ");
querySelect.append(linkTableName);
querySelect.append(" ");
querySelect.append(tmpVariable);
querySelect.append(" WHERE ");
querySelect.append(tmpVariable);
querySelect.append(".deleted = false");
querySelect.append(" AND ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpVariable);
querySelect.append(".");
querySelect.append("object1Id ");
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(" GROUP BY ");
querySelect.append(tmpVariable);
querySelect.append(".object1Id");
}
querySelect.append(") AS ");
querySelect.append(name);
querySelect.append(" ");
/* " (SELECT GROUP_CONCAT(tmp.data_id SEPARATOR '-')" + " FROM cover_link_node tmp" + " WHERE tmp.deleted = false" +
* " AND node.id = tmp.node_id" + " GROUP BY tmp.node_id) AS covers" + */
elemCount.inc();
count.inc();
}
@Override
public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, 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];
if (objectClass == Long.class) {
generateConcatQuerry(tableName, field, querrySelect, querry, name, elemCount, options);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
@@ -135,31 +183,46 @@ public class AddOnManyToMany implements DataAccessAddOn {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
generateConcatQuerry(tableName, field, querrySelect, querry, name, elemCount, options);
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
}
}
@Override
public void fillFromQuerry(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);
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;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR);
} else if (foreignKeyType == Long.class) {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
@@ -169,7 +232,28 @@ 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 QueryInList<>(idField, childs), null);
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == UUID.class) {
final List<UUID> idList = DataAccess.getListOfRawUUIDs(rs, count.value);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
final String idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass));
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
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)));
if (foreignData == null) {
return;
}
@@ -181,29 +265,278 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
}
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 LinkTable insertElement = new LinkTable(localKey, remoteKey);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
DataAccess.insert(insertElement, options);
}
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);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final QueryAnd condition = new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey));
return DataAccess.deleteWhere(LinkTable.class, condition, options);
@Override
public boolean isUpdateAsync(final Field field) {
return true;
}
@Override
public void createTables(final String tableName, 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 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];
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() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
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)));
});
asyncInsert(tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
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)));
});
asyncInsert(tableName, localKey, field, data, actions);
} else {
actions.add(() -> {
DataAccess.deleteWhere(LinkTableUUIDUUID.class, new OverrideTableName(linkTableName),
new Condition(new QueryCondition("object1Id", "=", localKeyUUID)));
});
asyncInsert(tableName, localKey, field, data, actions);
}
}
}
@Override
public boolean isInsertAsync(final Field field) {
return true;
}
@Override
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;
}
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];
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() + ">");
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
if (localKey instanceof final Long localKeyLong) {
if (objectClass == Long.class) {
// ========================================================
// == Link a "Long" primary Key with List<Long>
// ========================================================
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableLongLong> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableLongLong(localKeyLong, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
DataAccess.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
} else {
// ========================================================
// == Link a "Long" primary Key with List<UUID>
// ========================================================
@SuppressWarnings("unchecked")
final List<UUID> dataCasted = (List<UUID>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableLongUUID> insertElements = new ArrayList<>();
for (final UUID remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableLongUUID(localKeyLong, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
DataAccess.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
}
} else if (localKey instanceof final UUID localKeyUUID) {
if (objectClass == Long.class) {
// ========================================================
// == Link a "UUID" primary Key with List<Long>
// ========================================================
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableUUIDLong> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableUUIDLong(localKeyUUID, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
DataAccess.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
} else {
// ========================================================
// == Link a "UUID" primary Key with List<UUID>
// ========================================================
@SuppressWarnings("unchecked")
final List<UUID> dataCasted = (List<UUID>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableUUIDUUID> insertElements = new ArrayList<>();
for (final UUID remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
insertElements.add(new LinkTableUUIDUUID(localKeyUUID, remoteKey));
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
DataAccess.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
}
} else {
throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long or UUID: "
+ localKey.getClass().getCanonicalName());
}
}
@Override
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];
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() + ">");
}
DataAccess.drop(LinkTableLongLong.class, new OverrideTableName(linkTableName));
}
@Override
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];
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() + ">");
}
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 {
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
* DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); } */
final LinkTableLongLong insertElement = new LinkTableLongLong(localKey, remoteKey);
DataAccess.insert(insertElement, new OverrideTableName(linkTableName));
}
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))));
}
@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 {
final String linkTableName = generateLinkTableNameField(tableName, field);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final List<String> sqlCommand = DataFactory.createTable(LinkTable.class, options);
postActionList.addAll(sqlCommand);
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() + ">");
}
final Class<?> primaryType = primaryField.getType();
if (primaryType == Long.class) {
if (objectClass == Long.class) {
final List<String> sqlCommand = DataFactory.createTable(LinkTableLongLong.class, options);
postActionList.addAll(sqlCommand);
} else {
final List<String> sqlCommand = DataFactory.createTable(LinkTableLongUUID.class, options);
postActionList.addAll(sqlCommand);
}
} else if (primaryType == UUID.class) {
if (objectClass == Long.class) {
final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDLong.class, options);
postActionList.addAll(sqlCommand);
} else {
final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDUUID.class, options);
postActionList.addAll(sqlCommand);
}
} else {
throw new DataAccessException("Can not ManyToMany with other than primary key type Long or UUID Model: "
+ primaryType.getCanonicalName());
}
}
}

View File

@@ -5,6 +5,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut;
@@ -14,6 +15,7 @@ import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,12 +32,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public String getSQLFieldType(final Field elem) throws Exception {
final String fieldName = AnnotationTools.getFieldName(elem);
public String getSQLFieldType(final Field field) throws Exception {
final String fieldName = AnnotationTools.getFieldName(field);
try {
return DataFactory.convertTypeInSQL(Long.class, fieldName);
return DataFactory.convertTypeInSQL(field.getType(), fieldName);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
@@ -43,18 +44,42 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean isCompatibleField(final Field elem) {
final ManyToOne decorators = elem.getDeclaredAnnotation(ManyToOne.class);
return decorators != null;
return elem.getDeclaredAnnotation(ManyToOne.class) != null;
}
@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) {
ps.setNull(iii.value, Types.BIGINT);
if (field.getType() == Long.class) {
ps.setNull(iii.value, Types.BIGINT);
} else if (field.getType() == Integer.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == Short.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == String.class) {
ps.setNull(iii.value, Types.VARCHAR);
} else if (field.getType() == UUID.class) {
ps.setNull(iii.value, Types.BINARY);
}
} else if (field.getType() == Long.class) {
final Long dataLong = (Long) data;
ps.setLong(iii.value, dataLong);
final Long dataTyped = (Long) data;
ps.setLong(iii.value, dataTyped);
} else if (field.getType() == Integer.class) {
final Integer dataTyped = (Integer) data;
ps.setInt(iii.value, dataTyped);
} else if (field.getType() == Short.class) {
final Short dataTyped = (Short) data;
ps.setShort(iii.value, dataTyped);
} else if (field.getType() == String.class) {
final String dataTyped = (String) data;
ps.setString(iii.value, dataTyped);
} else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data;
LOGGER.info("Generate UUTD for DB: {}", dataTyped);
final byte[] dataByte = UuidUtils.asBytes(dataTyped);
ps.setBytes(iii.value, dataByte);
} else {
final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data);
@@ -71,7 +96,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean canInsert(final Field field) {
if (field.getType() == Long.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);
@@ -81,9 +107,16 @@ public class AddOnManyToOne implements DataAccessAddOn {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
if (field.getType() == Long.class) {
final Class<?> classType = field.getType();
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);
@@ -94,40 +127,48 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception {
if (field.getType() == Long.class) {
querrySelect.append(" ");
querrySelect.append(tableName);
querrySelect.append(".");
querrySelect.append(name);
elemCount.inc();
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(".");
querySelect.append(name);
count.inc();
return;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
// TODO: rework this to have a lazy mode ...
DataAccess.generateSelectField(querrySelect, querry, field.getType(), options, elemCount);
DataAccess.generateSelectField(querySelect, query, field.getType(), options, count);
final Class<?> subType = field.getType();
final String subTableName = AnnotationTools.getTableName(subType);
final Field idField = AnnotationTools.getFieldOfId(subType);
querry.append("LEFT OUTER JOIN `");
querry.append(subTableName);
querry.append("` ON ");
querry.append(subTableName);
querry.append(".");
querry.append(AnnotationTools.getFieldName(idField));
querry.append(" = ");
querry.append(tableName);
querry.append(".");
querry.append(AnnotationTools.getFieldName(field));
query.append("LEFT OUTER JOIN `");
query.append(subTableName);
query.append("` ON ");
query.append(subTableName);
query.append(".");
query.append(AnnotationTools.getFieldName(idField));
query.append(" = ");
query.append(tableName);
query.append(".");
query.append(AnnotationTools.getFieldName(field));
} else {
querrySelect.append(" ");
querrySelect.append(tableName);
querrySelect.append(".");
querrySelect.append(name);
elemCount.inc();
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
}
@@ -136,7 +177,13 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
@Override
public void fillFromQuerry(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();
@@ -145,6 +192,39 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
return;
}
if (field.getType() == Integer.class) {
final Integer foreignKey = rs.getInt(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == Short.class) {
final Short foreignKey = rs.getShort(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == String.class) {
final String foreignKey = rs.getString(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == UUID.class) {
final byte[] tmp = rs.getBytes(count.value);
count.inc();
if (!rs.wasNull()) {
final UUID foreignKey = UuidUtils.asUuid(tmp);
field.set(data, foreignKey);
}
return;
}
final Class<?> objectClass = field.getType();
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (decorators == null) {
@@ -153,7 +233,8 @@ 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);
}
@@ -179,8 +260,25 @@ public class AddOnManyToOne implements DataAccessAddOn {
// 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 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 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, 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, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, Long.class);
}
}
}

View File

@@ -75,7 +75,8 @@ 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 {
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) {
@@ -92,24 +93,41 @@ public class AddOnOneToMany implements DataAccessAddOn {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return false;
}
@Override
public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, final QueryOptions options) {
querrySelect.append(" ");
querrySelect.append(tableName);
querrySelect.append(".");
querrySelect.append(name);
elemCount.inc();
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) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
@Override
public void fillFromQuerry(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall)
throws SQLException, IllegalArgumentException, IllegalAccessException {
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()) {
@@ -120,8 +138,17 @@ public class AddOnOneToMany implements DataAccessAddOn {
// 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 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 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, Long.class);
}
}

View File

@@ -22,6 +22,8 @@ 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 = "-";
@@ -53,12 +55,14 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
@Override
public boolean isCompatibleField(final Field field) {
final SQLTableExternalForeinKeyAsList decorators = field.getDeclaredAnnotation(SQLTableExternalForeinKeyAsList.class);
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 {
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) {
@@ -72,37 +76,61 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
@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 false;
return true;
}
@Override
public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, final QueryOptions options) {
elemCount.inc();
querrySelect.append(" ");
querrySelect.append(tableName);
querrySelect.append(".");
querrySelect.append(name);
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 fillFromQuerry(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall)
throws SQLException, IllegalArgumentException, IllegalAccessException {
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 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);
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, String.class);
}
}

View File

@@ -1,25 +0,0 @@
package org.kar.archidata.dataAccess.addOn.model;
import org.kar.archidata.annotation.DataComment;
import org.kar.archidata.model.GenericDataSoftDelete;
import jakarta.persistence.Column;
public class LinkTable extends GenericDataSoftDelete {
public LinkTable() {
// nothing to do...
}
public LinkTable(final long object1Id, final long object2Id) {
this.object1Id = object1Id;
this.object2Id = object2Id;
}
@DataComment("Object reference 1")
@Column(nullable = false)
public Long object1Id;
@DataComment("Object reference 2")
@Column(nullable = false)
public Long object2Id;
}

View File

@@ -0,0 +1,25 @@
package org.kar.archidata.dataAccess.addOn.model;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableLongLong extends UUIDGenericDataSoftDelete {
public LinkTableLongLong() {
// nothing to do...
}
public LinkTableLongLong(final long object1Id, final long object2Id) {
this.object1Id = object1Id;
this.object2Id = object2Id;
}
@Schema(description = "Object reference 1")
@Column(nullable = false)
public Long object1Id;
@Schema(description = "Object reference 2")
@Column(nullable = false)
public Long object2Id;
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableLongUUID extends UUIDGenericDataSoftDelete {
public LinkTableLongUUID() {
// nothing to do...
}
public LinkTableLongUUID(final long object1Id, final UUID object2Id) {
this.object1Id = object1Id;
this.object2Id = object2Id;
}
@Schema(description = "Object reference 1")
@Column(nullable = false)
public Long object1Id;
@Schema(description = "Object reference 2")
@Column(nullable = false)
public UUID object2Id;
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableUUIDLong extends UUIDGenericDataSoftDelete {
public LinkTableUUIDLong() {
// nothing to do...
}
public LinkTableUUIDLong(final UUID object1Id, final long object2Id) {
this.object1Id = object1Id;
this.object2Id = object2Id;
}
@Schema(description = "Object reference 1")
@Column(nullable = false)
public UUID object1Id;
@Schema(description = "Object reference 2")
@Column(nullable = false)
public Long object2Id;
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.dataAccess.addOn.model;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericDataSoftDelete;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTableUUIDUUID extends UUIDGenericDataSoftDelete {
public LinkTableUUIDUUID() {
// nothing to do...
}
public LinkTableUUIDUUID(final UUID object1Id, final UUID object2Id) {
this.object1Id = object1Id;
this.object2Id = object2Id;
}
@Schema(description = "Object reference 1")
@Column(nullable = false)
public UUID object1Id;
@Schema(description = "Object reference 2")
@Column(nullable = false)
public UUID object2Id;
}

View File

@@ -0,0 +1,28 @@
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 TableCoversLongLong() {
// nothing to do...
}
public TableCoversLongLong(final Long id, final List<Long> covers) {
this.id = id;
this.covers = covers;
}
@Column(nullable = false)
@Id
public Long id;
@DataJson()
@Column(nullable = false)
public List<Long> covers;
}

View File

@@ -0,0 +1,29 @@
package org.kar.archidata.dataAccess.addOn.model;
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 TableCoversLongUUID() {
// nothing to do...
}
public TableCoversLongUUID(final Long id, final List<UUID> covers) {
this.id = id;
this.covers = covers;
}
@Column(nullable = false)
@Id
public Long id;
@DataJson()
@Column(nullable = false)
public List<UUID> covers;
}

View File

@@ -0,0 +1,29 @@
package org.kar.archidata.dataAccess.addOn.model;
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 TableCoversUUIDLong() {
// nothing to do...
}
public TableCoversUUIDLong(final UUID id, final List<Long> covers) {
this.id = id;
this.covers = covers;
}
@Column(nullable = false)
@Id
public UUID id;
@DataJson()
@Column(nullable = false)
public List<Long> covers;
}

View File

@@ -0,0 +1,29 @@
package org.kar.archidata.dataAccess.addOn.model;
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 TableCoversUUIDUUID() {
// nothing to do...
}
public TableCoversUUIDUUID(final UUID id, final List<UUID> covers) {
this.id = id;
this.covers = covers;
}
@Column(nullable = false)
@Id
public UUID id;
@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

@@ -0,0 +1,14 @@
package org.kar.archidata.dataAccess.exportTools;
import java.util.ArrayList;
import java.util.List;
public class TableQuery {
public final List<TableQueryTypes> headers;
public final List<List<Object>> values = new ArrayList<>();
public TableQuery(final List<TableQueryTypes> headers) {
this.headers = headers;
}
}

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.dataAccess.exportTools;
public class TableQueryTypes {
public Class<?> type;
public String name;
public String title;
public TableQueryTypes(final Class<?> type, final String name, final String title) {
this.type = type;
this.name = name;
this.title = title;
}
public TableQueryTypes(final Class<?> type, final String name) {
this.type = type;
this.name = name;
this.title = name;
}
}

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

@@ -2,12 +2,19 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.annotation.AnnotationTools;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckFunctionInterface {
/** This function implementation is design to check if the updated class is valid of not for insertion
* @param baseName NAme of the object to be precise with the use of what fail.
* @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(Object data, List<String> filterValue) throws Exception;
void check(final String baseName, Object data, List<String> filterValue) throws Exception;
default void checkAll(final String baseName, final Object data) throws Exception {
check(baseName, data, AnnotationTools.getAllFieldsNames(data.getClass()));
}
}

View File

@@ -0,0 +1,12 @@
package org.kar.archidata.dataAccess.options;
import java.util.List;
/** 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) {
}
}

View File

@@ -0,0 +1,360 @@
package org.kar.archidata.dataAccess.options;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
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.exception.DataAccessException;
import org.kar.archidata.exception.InputException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.Size;
public class CheckJPA<T> implements CheckFunctionInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(CheckJPA.class);
private final Class<?> clazz;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckInterface<K> {
/** This function implementation is design to check if the updated class is valid of not for insertion
* @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;
}
protected Map<String, List<CheckInterface<T>>> checking = null;
protected void add(final String field, final CheckInterface<T> checkFunction) {
List<CheckInterface<T>> actions = this.checking.get(field);
if (actions == null) {
actions = new ArrayList<>();
this.checking.put(field, actions);
}
actions.add(checkFunction);
}
public CheckJPA(final Class<T> clazz) {
this.clazz = clazz;
}
public void initialize() throws Exception {
if (this.checking != null) {
return;
}
try {
this.checking = new HashMap<>();
// create Table:
final List<String> primaryKeys = new ArrayList<>();
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");
});
}
if (AnnotationTools.getConstraintsNotNull(field)) {
add(fieldName, (final String baseName, final T data) -> {
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) -> {
throw new InputException(baseName + fieldName, "It is forbidden to change this field");
});
}
final Class<?> type = field.getType();
if (type == Long.class || type == long.class) {
final Long maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValue = AnnotationTools.getConstraintsMin(field);
if (minValue != null) {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
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);
}
});
}
} else if (type == Integer.class || type == int.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final int maxValue = maxValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final int minValue = minValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
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);
}
});
}
} 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) -> {
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);
}
});
}
} else if (type == Boolean.class || type == boolean.class) {
} else if (type == Float.class || type == float.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final float maxValue = maxValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final float minValue = minValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
} else if (type == Double.class || type == double.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final double maxValue = maxValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final double minValue = minValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
} else if (type == Date.class || type == Timestamp.class) {
} else if (type == LocalDate.class) {
} else if (type == LocalTime.class) {
} else if (type == String.class) {
final int maxSizeString = AnnotationTools.getLimitSize(field);
if (maxSizeString > 0) {
add(fieldName, (final String baseName, final T data) -> {
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);
}
});
}
final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) {
add(fieldName, (final String baseName, final T data) -> {
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());
}
if (elemTyped.length() < limitSize.min()) {
throw new InputException(baseName + fieldName,
"Too small size (constraints) must be >= " + limitSize.max());
}
});
}
final String patternString = AnnotationTools.getConstraintsPattern(field);
if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString);
add(fieldName, (final String baseName, final T data) -> {
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
+ "'");
}
});
}
} else if (type == JsonValue.class) {
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));
});
}
} else if (type.isEnum()) {
// nothing to do.
}
// 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))));
if (other != null) {
throw new InputException(baseName + fieldName, "Name already exist in the DB");
}
});
}
}
} catch (final Exception ex) {
this.checking = null;
throw ex;
}
}
@Override
public void check(final String baseName, final Object data, final List<String> filterValue) throws Exception {
if (this.checking == null) {
initialize();
}
if (!(this.clazz.isAssignableFrom(data.getClass()))) {
throw new DataAccessException("Incompatatyble type of Object" + data.getClass().getCanonicalName());
}
@SuppressWarnings("unchecked")
final T dataCasted = (T) data;
for (final String filter : filterValue) {
final List<CheckInterface<T>> actions = this.checking.get(filter);
if (actions == null) {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(baseName, dataCasted);
}
}
checkTyped(dataCasted, filterValue);
}
public void checkTyped(final T data, final List<String> filterValue) throws Exception {
// nothing to do ...
}
}

View File

@@ -0,0 +1,65 @@
package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.QueryItem;
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 Condition extends QueryOption {
public final QueryItem condition;
public Condition(final QueryItem items) {
this.condition = items;
}
public Condition() {
this.condition = null;
}
public void generateQuery(final StringBuilder query, final String tableName) {
if (this.condition != null) {
this.condition.generateQuery(query, tableName);
}
}
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
if (this.condition != null) {
this.condition.injectQuery(ps, iii);
}
}
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);
}
// Check if we have a condition to generate
if (this.condition == null) {
if (exclude_deleted && deletedFieldName != null) {
query.append(" WHERE ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false \n");
}
return;
}
query.append(" WHERE (");
this.condition.generateQuery(query, tableName);
query.append(") ");
if (exclude_deleted && deletedFieldName != null) {
query.append("AND ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false ");
}
query.append("\n");
}
}

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

@@ -0,0 +1,48 @@
package org.kar.archidata.dataAccess.options;
import java.io.IOException;
import java.util.List;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.db.DBEntry;
public class DBInterfaceOption extends QueryOption {
private DBEntry entry = null;
private final DBConfig config;
private final boolean root;
public DBInterfaceOption(final DBConfig config) {
this.config = config;
this.root = false;
}
public DBInterfaceOption(final DBConfig config, final boolean root) {
this.config = config;
this.root = root;
}
public DBEntry getEntry(final QueryOptions options) throws IOException {
if (this.entry == null) {
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);
this.entry = DBEntry.createInterface(this.config, isRoot.size() == 1 && isRoot.get(0).getRoot());
}
return this.entry;
}
public boolean getRoot() {
return this.root;
}
public static DBEntry getAutoEntry(final QueryOptions options) throws IOException {
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.get(0).getEntry(options);
}
}
}

View File

@@ -0,0 +1,14 @@
package org.kar.archidata.dataAccess.options;
public class DBInterfaceRoot extends QueryOption {
private final boolean root;
public DBInterfaceRoot(boolean root) {
this.root = root;
}
public boolean getRoot() {
return this.root;
}
}

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.dataAccess.options;
import java.util.List;
/** 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;
public FilterValue(final List<String> filterValue) {
this.filterValue = filterValue;
}
public FilterValue(final String... filterValue) {
this.filterValue = List.of(filterValue);
}
public List<String> getValues() {
return this.filterValue;
}
}

View File

@@ -0,0 +1,41 @@
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;
public GroupBy(final List<String> childs) {
this.childs = childs;
}
public GroupBy(final String... childs) {
this.childs = List.of(childs);
}
public void generateQuery(final StringBuilder query, final String tableName) {
if (this.childs.size() == 0) {
return;
}
query.append(" GROUP BY ");
boolean first = true;
for (final String elem : this.childs) {
if (first) {
first = false;
} else {
query.append(", ");
}
// query.append("`");
query.append(elem);
// query.append("` ");
}
query.append("\n");
}
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
// nothing to add.
}
}

View File

@@ -1,17 +1,23 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
import java.sql.PreparedStatement;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
/** 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 Limit extends QueryOption {
private final int limit;
protected final long limit;
public Limit(final int limit) {
public Limit(final long limit) {
this.limit = limit;
}
public int getLimit() {
return this.limit;
public void generateQuery(final StringBuilder query, final String tableName) {
query.append(" LIMIT ? \n");
}
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
DataAccess.addElement(ps, this.limit, iii);
iii.inc();
}
}

View File

@@ -0,0 +1,43 @@
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;
public OrderBy(final List<OrderItem> childs) {
this.childs = childs;
}
public OrderBy(final OrderItem... childs) {
this.childs = List.of(childs);
}
public void generateQuery(final StringBuilder query, final String tableName) {
if (this.childs.size() == 0) {
return;
}
query.append(" ORDER BY ");
boolean first = true;
for (final OrderItem elem : this.childs) {
if (first) {
first = false;
} else {
query.append(", ");
}
// query.append("`");
query.append(elem.value);
// query.append("`");
query.append(" ");
query.append(elem.order.toString());
}
query.append("\n");
}
public void injectQuery(final PreparedStatement ps, final CountInOut iii) throws Exception {
// nothing to add.
}
}

View File

@@ -0,0 +1,16 @@
package org.kar.archidata.dataAccess.options;
public class OrderItem {
public enum Order {
ASC, DESC
};
public final String value;
public final Order order;
public OrderItem(final String value, final Order order) {
this.value = value;
this.order = 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

@@ -0,0 +1,14 @@
package org.kar.archidata.dataAccess.options;
/** Internal option that permit to transmit the Key when updating the ManyToMany values (first step). */
public class TransmitKey extends QueryOption {
private final Object key;
public TransmitKey(final Object key) {
this.key = key;
}
public Object getKey() {
return this.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,18 +50,20 @@ 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());
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}
}
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());
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
}
}

View File

@@ -5,7 +5,7 @@ import java.util.UUID;
public class RESTErrorResponseExeption extends Exception {
public UUID uuid;
public String time;
public String error;
public String name;
public String message;
public int status;
public String statusMessage;
@@ -13,16 +13,17 @@ public class RESTErrorResponseExeption extends Exception {
public RESTErrorResponseExeption() {
this.uuid = null;
this.time = null;
this.error = null;
this.name = null;
this.message = null;
this.status = 0;
this.statusMessage = null;
}
public RESTErrorResponseExeption(final UUID uuid, final String time, final String error, 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.error = error;
this.name = name;
this.message = message;
this.status = status;
this.statusMessage = statusMessage;
@@ -30,8 +31,9 @@ public class RESTErrorResponseExeption extends Exception {
@Override
public String toString() {
return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", error=" + this.error + ", 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,8 @@
package org.kar.archidata.externalRestApi;
public class PythonGenerateApi {
public static void generateApi(final AnalyzeApi api) {
}
}

View File

@@ -0,0 +1,433 @@
package org.kar.archidata.externalRestApi;
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 org.kar.archidata.dataAccess.DataExport;
import org.kar.archidata.externalRestApi.TsClassElement.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.kar.archidata.externalRestApi.model.RestTypeRequest;
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 boolean writeMode) 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 boolean writeMode) throws IOException {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NATIVE) {
imports.add(model);
}
if (tsModel.nativeType == DefinedPosition.BASIC) {
return tsModel.tsTypeName;
}
if (writeMode) {
return tsModel.tsTypeName + "Write";
}
return tsModel.tsTypeName;
}
public static String generateClassMapModelTypescript(
final ClassMapModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final boolean writeMode) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("{[key: ");
out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, writeMode));
out.append("]: ");
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, writeMode));
out.append(";}");
return out.toString();
}
public static String generateClassListModelTypescript(
final ClassListModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final boolean writeMode) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, writeMode));
out.append("[]");
return out.toString();
}
public static String generateClassModelTypescript(
final ClassModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final boolean writeMode) throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, tsGroup, imports, writeMode);
}
if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, tsGroup, imports, writeMode);
}
if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, tsGroup, imports, writeMode);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, tsGroup, imports, writeMode);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final boolean writeMode) 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, writeMode);
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\tcallback,");
}
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, false));
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, false));
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, writeImports,
true));
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, writeImports, true));
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\tcallback?: 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,
false);
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 {
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 String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, imports,
false);
if (!"void".equals(returnType)) {
data.append(", is");
data.append(returnType);
}
}
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 List<String> finalImportList = new ArrayList<>();
for (final ClassModel model : imports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportList.add(tsModel.tsTypeName);
}
for (final ClassModel model : isImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportList.add("is" + tsModel.tsTypeName);
}
for (final ClassModel model : zodImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportList.add("Zod" + tsModel.tsTypeName);
}
for (final ClassModel model : writeImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportList.add(tsModel.tsTypeName + "Write");
}
Collections.sort(finalImportList);
if (finalImportList.size() != 0) {
out.append("import {");
for (final String elem : finalImportList) {
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,473 @@
package org.kar.archidata.externalRestApi;
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 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 String elem : model.getListOfValues()) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append("'");
out.append(elem);
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 String elem : model.getListOfValues()) {
if (!first) {
out.append(",\n\t");
} else {
out.append("\n\t");
first = false;
}
out.append(elem);
out.append(" = '");
out.append(elem);
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);
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 String optionalTypeZod(final FieldProperty field) {
if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) {
return "";
}
if (field.notNull()) {
return "";
}
return ".optional()";
}
public String maxSizeZod(final FieldProperty field) {
final StringBuilder builder = new StringBuilder();
final Class<?> clazz = field.model().getOriginClasses();
if (field.limitSize() > 0 && clazz == String.class) {
builder.append(".max(");
builder.append(field.limitSize());
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");
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(""));
// 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");
out.append(generateZodInfer(this.tsTypeName + "Write", this.zodName + "Write"));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionWrite("Write"));
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.createDirectory(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;
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;
}
}

View File

@@ -0,0 +1,231 @@
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.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.dataAccess.DataFactoryTsApi;
import org.kar.archidata.externalRestApi.TsClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class TsGenerateApi {
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.object()", "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));
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 = DataFactoryTsApi.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,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);
}
}
}

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