diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..622c644 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,91 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + # push: + # branches: [ "main" ] + # pull_request: + # branches: [ "main" ] + schedule: + - cron: '26 2 * * 6' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml new file mode 100644 index 0000000..954e6c8 --- /dev/null +++ b/.github/workflows/maven-publish.yml @@ -0,0 +1,34 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: Maven Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..857c099 --- /dev/null +++ b/.github/workflows/maven.yml @@ -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: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 20 + uses: actions/setup-java@v3 + with: + java-version: '20' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: | + cd primekit-essentials + mvn -B clean package -DskipTests + + # 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 diff --git a/README.md b/README.md index de62dc2..8e1abb0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,43 @@ # PrimeKit + +[![StackShare](http://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](https://stackshare.io/shortthirdman-org/prime-kit) + PrimeKit + +## PrimeKit Essentials + +A Java common library + +![Github Created At](https://img.shields.io/github/created-at/shortthirdman-org/PrimeKit?logo=github&label=shortthirdman-org%2FPrimeKit&link=https%3A%2F%2Fgithub.com%2Fshortthirdman-org%2FPrimeKit) +![GitHub language count](https://img.shields.io/github/languages/count/shortthirdman-org/PrimeKit) +![GitHub commits since latest release](https://img.shields.io/github/commits-since/shortthirdman-org/PrimeKit/latest) + +![GitHub](https://img.shields.io/github/license/shortthirdman-org/PrimeKit) +![GitHub issues](https://img.shields.io/github/issues/shortthirdman-org/PrimeKit) +![GitHub closed issues](https://img.shields.io/github/issues-closed/shortthirdman-org/PrimeKit) +![GitHub Workflow Status (with event and branch)](https://img.shields.io/github/actions/workflow/status/shortthirdman-org/PrimeKit/release.yml?event=push&branch=main) + +## Tech Stack + +shortthirdman-org/PrimeKit is built on the following main stack: + + + +Full tech stack [here](/techstack.md) + + +## Community Resources + +* [Release a Java Module with JReleaser to Maven Central with GitHub Actions](https://foojay.io/today/how-to-release-a-java-module-with-jreleaser-to-maven-central-with-github-actions/) +* [Publish a Java Maven Project to the Maven Central Repository](https://foojay.io/today/how-to-publish-a-java-maven-project-to-the-maven-central-repository/) +* [Publish Artifacts to Maven Central](https://www.jetbrains.com/help/space/publish-artifacts-to-maven-central.html) + + +## Reference Documentation +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.2.6/maven-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.2.6/maven-plugin/reference/html/#build-image) +* [Spring Shell](https://spring.io/projects/spring-shell) + diff --git a/pom-default.xml b/pom-default.xml new file mode 100644 index 0000000..33ffa10 --- /dev/null +++ b/pom-default.xml @@ -0,0 +1,291 @@ + + + + 4.0.0 + + com.shortthirdman + sharedlibs + 0.0.1-SNAPSHOT + sharedlibs + https://www.github.com/shortthirdman-org/sharedlibs + + + UTF-8 + 1.8 + 1.8 + 1.8 + 3.19.0 + 5.6.10.Final + + + + + primary + Swetank Mohanty + swetank.mohanty@outlook.com + IBM India + UTC+530 + + Application Developer + + + + + + + Swetank Mohanty + swetank.mohanty@outlook.com + IBM India + UTC+530 + + + + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + + + junit + junit + 4.13.2 + test + + + commons-codec + commons-codec + 1.15 + + + commons-io + commons-io + 2.11.0 + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + + release + + + performRelease + false + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + install + + jar-no-fork + + + + + + + + + + reporting + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.4.2 + + + + + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://s01.oss.sonatype.org/ + true + + + + maven-compiler-plugin + 3.8.0 + + UTF-8 + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + true + true + + + + maven-surefire-plugin + 2.22.1 + + true + + + + org.apache.maven.surefire + surefire-junit47 + 3.0.0 + + + + + maven-deploy-plugin + 2.8.2 + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + + prepare-agent + + + + jacoco-report + test + + report + + + + jacoco-check + + check + + + + + PACKAGE + + + LINE + COVEREDRATIO + 0.65 + + + + + + + + + + + + diff --git a/primekit-essentials/.editorconfig b/primekit-essentials/.editorconfig new file mode 100644 index 0000000..86d1253 --- /dev/null +++ b/primekit-essentials/.editorconfig @@ -0,0 +1,319 @@ +root = true + +[*] +# charset = utf-8 +# end_of_line = crlf +# indent_size = 4 +# indent_style = space +# insert_final_newline = false +# max_line_length = 120 +# tab_width = 4 +# ij_continuation_indent_size = 8 +# ij_formatter_off_tag = @formatter:off +# ij_formatter_on_tag = @formatter:on +# ij_formatter_tags_enabled = true +# ij_smart_tabs = false +# ij_visual_guides = +# ij_wrap_on_typing = false + +[*.java] +# ij_java_align_consecutive_assignments = false +# ij_java_align_consecutive_variable_declarations = false +# ij_java_align_group_field_declarations = false +# ij_java_align_multiline_annotation_parameters = false +# ij_java_align_multiline_array_initializer_expression = false +# ij_java_align_multiline_assignment = false +# ij_java_align_multiline_binary_operation = false +# ij_java_align_multiline_chained_methods = false +# ij_java_align_multiline_deconstruction_list_components = true +# ij_java_align_multiline_extends_list = false +# ij_java_align_multiline_for = true +# ij_java_align_multiline_method_parentheses = false +# ij_java_align_multiline_parameters = true +# ij_java_align_multiline_parameters_in_calls = false +# ij_java_align_multiline_parenthesized_expression = false +# ij_java_align_multiline_records = true +# ij_java_align_multiline_resources = true +# ij_java_align_multiline_ternary_operation = false +# ij_java_align_multiline_text_blocks = false +# ij_java_align_multiline_throws_list = false +# ij_java_align_subsequent_simple_methods = false +# ij_java_align_throws_keyword = false +# ij_java_align_types_in_multi_catch = true +# ij_java_annotation_parameter_wrap = off +# ij_java_array_initializer_new_line_after_left_brace = false +# ij_java_array_initializer_right_brace_on_new_line = false +# ij_java_array_initializer_wrap = off +# ij_java_assert_statement_colon_on_next_line = false +# ij_java_assert_statement_wrap = off +# ij_java_assignment_wrap = off +# ij_java_binary_operation_sign_on_next_line = false +# ij_java_binary_operation_wrap = off +# ij_java_blank_lines_after_anonymous_class_header = 0 +# ij_java_blank_lines_after_class_header = 0 +# ij_java_blank_lines_after_imports = 1 +# ij_java_blank_lines_after_package = 1 +# ij_java_blank_lines_around_class = 1 +# ij_java_blank_lines_around_field = 0 +# ij_java_blank_lines_around_field_in_interface = 0 +# ij_java_blank_lines_around_initializer = 1 +# ij_java_blank_lines_around_method = 1 +# ij_java_blank_lines_around_method_in_interface = 1 +# ij_java_blank_lines_before_class_end = 0 +# ij_java_blank_lines_before_imports = 1 +# ij_java_blank_lines_before_method_body = 0 +# ij_java_blank_lines_before_package = 0 +# ij_java_block_brace_style = end_of_line +# ij_java_block_comment_add_space = false +# ij_java_block_comment_at_first_column = true +# ij_java_builder_methods = +# ij_java_call_parameters_new_line_after_left_paren = false +# ij_java_call_parameters_right_paren_on_new_line = false +# ij_java_call_parameters_wrap = off +# ij_java_case_statement_on_separate_line = true +# ij_java_catch_on_new_line = false +# ij_java_class_annotation_wrap = split_into_lines +# ij_java_class_brace_style = end_of_line +# ij_java_class_count_to_use_import_on_demand = 5 +# ij_java_class_names_in_javadoc = 1 +# ij_java_deconstruction_list_wrap = normal +# ij_java_do_not_indent_top_level_class_members = false +# ij_java_do_not_wrap_after_single_annotation = false +# ij_java_do_not_wrap_after_single_annotation_in_parameter = false +# ij_java_do_while_brace_force = never +# ij_java_doc_add_blank_line_after_description = true +# ij_java_doc_add_blank_line_after_param_comments = false +# ij_java_doc_add_blank_line_after_return = false +# ij_java_doc_add_p_tag_on_empty_lines = true +# ij_java_doc_align_exception_comments = true +# ij_java_doc_align_param_comments = true +# ij_java_doc_do_not_wrap_if_one_line = false +# ij_java_doc_enable_formatting = true +# ij_java_doc_enable_leading_asterisks = true +# ij_java_doc_indent_on_continuation = false +# ij_java_doc_keep_empty_lines = true +# ij_java_doc_keep_empty_parameter_tag = true +# ij_java_doc_keep_empty_return_tag = true +# ij_java_doc_keep_empty_throws_tag = true +# ij_java_doc_keep_invalid_tags = true +# ij_java_doc_param_description_on_new_line = false +# ij_java_doc_preserve_line_breaks = false +# ij_java_doc_use_throws_not_exception_tag = true +# ij_java_else_on_new_line = false +# ij_java_enum_constants_wrap = off +# ij_java_enum_field_annotation_wrap = off +# ij_java_extends_keyword_wrap = off +# ij_java_extends_list_wrap = off +# ij_java_field_annotation_wrap = split_into_lines +# ij_java_field_name_prefix = +# ij_java_field_name_suffix = +# ij_java_finally_on_new_line = false +# ij_java_for_brace_force = never +# ij_java_for_statement_new_line_after_left_paren = false +# ij_java_for_statement_right_paren_on_new_line = false +# ij_java_for_statement_wrap = off +# ij_java_generate_final_locals = false +# ij_java_generate_final_parameters = false +# ij_java_if_brace_force = never +# ij_java_imports_layout = *,|,javax.**,java.**,|,$* +# ij_java_indent_case_from_switch = true +# ij_java_insert_inner_class_imports = false +# ij_java_insert_override_annotation = true +# ij_java_keep_blank_lines_before_right_brace = 2 +# ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +# ij_java_keep_blank_lines_in_code = 2 +# ij_java_keep_blank_lines_in_declarations = 2 +# ij_java_keep_builder_methods_indents = false +# ij_java_keep_control_statement_in_one_line = true +# ij_java_keep_first_column_comment = true +# ij_java_keep_indents_on_empty_lines = false +# ij_java_keep_line_breaks = true +# ij_java_keep_multiple_expressions_in_one_line = false +# ij_java_keep_simple_blocks_in_one_line = false +# ij_java_keep_simple_classes_in_one_line = false +# ij_java_keep_simple_lambdas_in_one_line = false +# ij_java_keep_simple_methods_in_one_line = false +# ij_java_label_indent_absolute = false +# ij_java_label_indent_size = 0 +# ij_java_lambda_brace_style = end_of_line +# ij_java_layout_static_imports_separately = true +# ij_java_line_comment_add_space = false +# ij_java_line_comment_add_space_on_reformat = false +# ij_java_line_comment_at_first_column = true +# ij_java_local_variable_name_prefix = +# ij_java_local_variable_name_suffix = +# ij_java_method_annotation_wrap = split_into_lines +# ij_java_method_brace_style = end_of_line +# ij_java_method_call_chain_wrap = off +# ij_java_method_parameters_new_line_after_left_paren = false +# ij_java_method_parameters_right_paren_on_new_line = false +# ij_java_method_parameters_wrap = off +# ij_java_modifier_list_wrap = false +# ij_java_multi_catch_types_wrap = normal +# ij_java_names_count_to_use_import_on_demand = 3 +# ij_java_new_line_after_lparen_in_annotation = false +# ij_java_new_line_after_lparen_in_deconstruction_pattern = true +# ij_java_new_line_after_lparen_in_record_header = false +# ij_java_new_line_when_body_is_presented = false +# ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +# ij_java_parameter_annotation_wrap = off +# ij_java_parameter_name_prefix = +# ij_java_parameter_name_suffix = +# ij_java_parentheses_expression_new_line_after_left_paren = false +# ij_java_parentheses_expression_right_paren_on_new_line = false +# ij_java_place_assignment_sign_on_next_line = false +# ij_java_prefer_longer_names = true +# ij_java_prefer_parameters_wrap = false +# ij_java_record_components_wrap = normal +# ij_java_repeat_annotations = +# ij_java_repeat_synchronized = true +# ij_java_replace_instanceof_and_cast = false +# ij_java_replace_null_check = true +# ij_java_replace_sum_lambda_with_method_ref = true +# ij_java_resource_list_new_line_after_left_paren = false +# ij_java_resource_list_right_paren_on_new_line = false +# ij_java_resource_list_wrap = off +# ij_java_rparen_on_new_line_in_annotation = false +# ij_java_rparen_on_new_line_in_deconstruction_pattern = true +# ij_java_rparen_on_new_line_in_record_header = false +# ij_java_space_after_closing_angle_bracket_in_type_argument = false +# ij_java_space_after_colon = true +# ij_java_space_after_comma = true +# ij_java_space_after_comma_in_type_arguments = true +# ij_java_space_after_for_semicolon = true +# ij_java_space_after_quest = true +# ij_java_space_after_type_cast = true +# ij_java_space_before_annotation_array_initializer_left_brace = false +# ij_java_space_before_annotation_parameter_list = false +# ij_java_space_before_array_initializer_left_brace = false +# ij_java_space_before_catch_keyword = true +# ij_java_space_before_catch_left_brace = true +# ij_java_space_before_catch_parentheses = true +# ij_java_space_before_class_left_brace = true +# ij_java_space_before_colon = true +# ij_java_space_before_colon_in_foreach = true +# ij_java_space_before_comma = false +# ij_java_space_before_deconstruction_list = false +# ij_java_space_before_do_left_brace = true +# ij_java_space_before_else_keyword = true +# ij_java_space_before_else_left_brace = true +# ij_java_space_before_finally_keyword = true +# ij_java_space_before_finally_left_brace = true +# ij_java_space_before_for_left_brace = true +# ij_java_space_before_for_parentheses = true +# ij_java_space_before_for_semicolon = false +# ij_java_space_before_if_left_brace = true +# ij_java_space_before_if_parentheses = true +# ij_java_space_before_method_call_parentheses = false +# ij_java_space_before_method_left_brace = true +# ij_java_space_before_method_parentheses = false +# ij_java_space_before_opening_angle_bracket_in_type_parameter = false +# ij_java_space_before_quest = true +# ij_java_space_before_switch_left_brace = true +# ij_java_space_before_switch_parentheses = true +# ij_java_space_before_synchronized_left_brace = true +# ij_java_space_before_synchronized_parentheses = true +# ij_java_space_before_try_left_brace = true +# ij_java_space_before_try_parentheses = true +# ij_java_space_before_type_parameter_list = false +# ij_java_space_before_while_keyword = true +# ij_java_space_before_while_left_brace = true +# ij_java_space_before_while_parentheses = true +# ij_java_space_inside_one_line_enum_braces = false +# ij_java_space_within_empty_array_initializer_braces = false +# ij_java_space_within_empty_method_call_parentheses = false +# ij_java_space_within_empty_method_parentheses = false +# ij_java_spaces_around_additive_operators = true +# ij_java_spaces_around_annotation_eq = true +# ij_java_spaces_around_assignment_operators = true +# ij_java_spaces_around_bitwise_operators = true +# ij_java_spaces_around_equality_operators = true +# ij_java_spaces_around_lambda_arrow = true +# ij_java_spaces_around_logical_operators = true +# ij_java_spaces_around_method_ref_dbl_colon = false +# ij_java_spaces_around_multiplicative_operators = true +# ij_java_spaces_around_relational_operators = true +# ij_java_spaces_around_shift_operators = true +# ij_java_spaces_around_type_bounds_in_type_parameters = true +# ij_java_spaces_around_unary_operator = false +# ij_java_spaces_within_angle_brackets = false +# ij_java_spaces_within_annotation_parentheses = false +# ij_java_spaces_within_array_initializer_braces = false +# ij_java_spaces_within_braces = false +# ij_java_spaces_within_brackets = false +# ij_java_spaces_within_cast_parentheses = false +# ij_java_spaces_within_catch_parentheses = false +# ij_java_spaces_within_deconstruction_list = false +# ij_java_spaces_within_for_parentheses = false +# ij_java_spaces_within_if_parentheses = false +# ij_java_spaces_within_method_call_parentheses = false +# ij_java_spaces_within_method_parentheses = false +# ij_java_spaces_within_parentheses = false +# ij_java_spaces_within_record_header = false +# ij_java_spaces_within_switch_parentheses = false +# ij_java_spaces_within_synchronized_parentheses = false +# ij_java_spaces_within_try_parentheses = false +# ij_java_spaces_within_while_parentheses = false +# ij_java_special_else_if_treatment = true +# ij_java_static_field_name_prefix = +# ij_java_static_field_name_suffix = +# ij_java_subclass_name_prefix = +# ij_java_subclass_name_suffix = Impl +# ij_java_switch_expressions_wrap = normal +# ij_java_ternary_operation_signs_on_next_line = false +# ij_java_ternary_operation_wrap = off +# ij_java_test_name_prefix = +# ij_java_test_name_suffix = Test +# ij_java_throws_keyword_wrap = off +# ij_java_throws_list_wrap = off +# ij_java_use_external_annotations = false +# ij_java_use_fq_class_names = false +# ij_java_use_relative_indents = false +# ij_java_use_single_class_imports = true +# ij_java_variable_annotation_wrap = off +# ij_java_visibility = public +# ij_java_while_brace_force = never +# ij_java_while_on_new_line = false +# ij_java_wrap_comments = false +# ij_java_wrap_first_method_in_call_chain = false +# ij_java_wrap_long_lines = false +# ij_java_wrap_semicolon_after_call_chain = false + +[.editorconfig] +# ij_editorconfig_align_group_field_declarations = false +# ij_editorconfig_space_after_colon = false +# ij_editorconfig_space_after_comma = true +# ij_editorconfig_space_before_colon = false +# ij_editorconfig_space_before_comma = false +# ij_editorconfig_spaces_around_assignment_operators = true + +[{*.markdown,*.md}] +# ij_markdown_force_one_space_after_blockquote_symbol = true +# ij_markdown_force_one_space_after_header_symbol = true +# ij_markdown_force_one_space_after_list_bullet = true +# ij_markdown_force_one_space_between_words = true +# ij_markdown_format_tables = true +# ij_markdown_insert_quote_arrows_on_wrap = true +# ij_markdown_keep_indents_on_empty_lines = false +# ij_markdown_keep_line_breaks_inside_text_blocks = true +# ij_markdown_max_lines_around_block_elements = 1 +# ij_markdown_max_lines_around_header = 1 +# ij_markdown_max_lines_between_paragraphs = 1 +# ij_markdown_min_lines_around_block_elements = 1 +# ij_markdown_min_lines_around_header = 1 +# ij_markdown_min_lines_between_paragraphs = 1 +# ij_markdown_wrap_text_if_long = true +# ij_markdown_wrap_text_inside_blockquotes = true + +[{*.yaml,*.yml}] +# indent_size = 2 +# ij_yaml_align_values_properties = do_not_align +# ij_yaml_autoinsert_sequence_marker = true +# ij_yaml_block_mapping_on_new_line = false +# ij_yaml_indent_sequence_value = true +# ij_yaml_keep_indents_on_empty_lines = false +# ij_yaml_keep_line_breaks = true +# ij_yaml_sequence_on_new_line = false +# ij_yaml_space_before_colon = false +# ij_yaml_spaces_within_braces = true +# ij_yaml_spaces_within_brackets = true diff --git a/primekit-essentials/.gitignore b/primekit-essentials/.gitignore new file mode 100644 index 0000000..0e13eeb --- /dev/null +++ b/primekit-essentials/.gitignore @@ -0,0 +1,11 @@ +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar diff --git a/primekit-essentials/.mvn/maven.config b/primekit-essentials/.mvn/maven.config new file mode 100644 index 0000000..31c8930 --- /dev/null +++ b/primekit-essentials/.mvn/maven.config @@ -0,0 +1 @@ +--no-transfer-progress \ No newline at end of file diff --git a/primekit-essentials/.mvn/wrapper/maven-wrapper.properties b/primekit-essentials/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..e70e7bc --- /dev/null +++ b/primekit-essentials/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/primekit-essentials/pom.xml b/primekit-essentials/pom.xml new file mode 100644 index 0000000..366326f --- /dev/null +++ b/primekit-essentials/pom.xml @@ -0,0 +1,344 @@ + + + + 4.0.0 + + com.shortthirdman + primekit-essentials + 1.0.0-SNAPSHOT + primekit-essentials + jar + PrimeKit Essentials + https://www.github.com/shortthirdman-org/PrimeKit + + + UTF-8 + 21 + ${java.version} + ${java.version} + ${java.version} + 1.18.30 + 6.1.6 + 3.2.5 + 2.15.4 + 4.0.2 + 3.25.3 + 42.7.3 + 5.10.2 + 0.8.9 + + + + + primary + Swetank Mohanty + swetank.mohanty@outlook.com + ShortThirdMan + UTC+530 + + Application Developer + Product Owner + + + + + + + Swetank Mohanty + swetank.mohanty@outlook.com + ShortThirdMan + UTC+530 + + + + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + jakarta.xml.bind + jakarta.xml.bind-api + ${jakarta.xml-bind.version} + + + commons-codec + commons-codec + 1.15 + + + commons-io + commons-io + 2.11.0 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.projectlombok + lombok + ${lombok.version} + + + org.springframework + spring-core + ${springframework.version} + + + org.springframework + spring-beans + ${springframework.version} + + + org.springframework + spring-webmvc + ${springframework.version} + + + org.springframework + spring-aop + ${springframework.version} + + + org.springframework + spring-context-support + ${springframework.version} + + + org.springframework.security + spring-security-core + ${springframework.version} + + + org.springframework + spring-context + ${springframework.version} + + + org.springframework + spring-web + ${springframework.version} + + + org.springframework + spring-jpa + 2.0.8 + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework.data + spring-data-commons + ${spring-data.version} + + + org.hibernate.common + hibernate-commons-annotations + 5.1.2.Final + + + org.hibernate + hibernate-core + 5.6.15.Final + + + jakarta.persistence + jakarta.persistence-api + 3.2.0-M2 + + + com.fasterxml.jackson.core + jackson-core + ${fasterxml-jackson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${fasterxml-jackson.version} + + + + org.postgresql + postgresql + ${postgresql.version} + provided + + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit-jupiter.version} + test + + + org.junit.platform + junit-platform-runner + 1.10.2 + test + + + org.junit.platform + junit-platform-suite-engine + 1.9.1 + test + + + + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.13.0 + + false + UTF-8 + ${java.version} + ${java.version} + + + + maven-surefire-plugin + 3.2.5 + + + me.fabriciorby + maven-surefire-junit5-tree-reporter + 1.2.1 + + + + 1 + ${project.build.sourceEncoding} + brief + + + junit.jupiter.conditions.deactivate = * + junit.jupiter.extensions.autodetection.enabled = true + junit.jupiter.testinstance.lifecycle.default = per_class + junit.jupiter.execution.parallel.enabled = true + + + + false + 3.0 + false + true + true + true + + + false + UTF-8 + false + + + false + false + true + true + + + + + maven-jar-plugin + 3.0.2 + + + maven-deploy-plugin + 2.8.2 + + + maven-project-info-reports-plugin + 3.0.0 + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + ${project.build.directory}/jacoco.exec + ${project.reporting.outputDirectory}/jacocoHtml + 140 + ${project.name} ${project.version} + + + + default-prepare-agent + + prepare-agent + + initialize + + + default-report + + report + + test + + + jacoco-check + test + + check + + + + + PACKAGE + + + LINE + COVEREDRATIO + 0.6 + + + INSTRUCTION + COVEREDRATIO + 65% + + + + + + + + + + + + + diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/CounterNode.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/CounterNode.java new file mode 100644 index 0000000..f584975 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/CounterNode.java @@ -0,0 +1,22 @@ +package com.shortthirdman.primekit.essentials.common; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CounterNode { + + private int count; + private TreeNode node; + + public CounterNode(TreeNode node, int count) { + this.count = count; + this.node = node; + } + + private CounterNode(TreeNode left, TreeNode right, int count) { + this.count = count; + this.node = TreeNode.newTreeNode(count, left, right); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/GenericConstants.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/GenericConstants.java new file mode 100644 index 0000000..9b60bb5 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/GenericConstants.java @@ -0,0 +1,69 @@ +package com.shortthirdman.primekit.essentials.common; + +import lombok.Getter; + +@Getter +public enum GenericConstants { + + START("START"), + END("END"), + + COMMA_SPACE(", "), + COMMA(","), + DOT_PERIOD("."), + UNDERSCORE("_"), + + EMPTY_SPACE(""), + BLANK_SPACE(" "), + DEFAULT_DELIMITER(","), + DOUBLE_COLON("::"), + COLON(":"), + HYPHEN("-"), + SEMICOLON(";"), + PIPE("|"), + ASTERISK("*"), + + OPEN_PARENTHESIS("("), + CLOSED_PARENTHESIS(")"), + OPEN_BRACES("{"), + CLOSED_BRACES("}"), + + SINGLE_QUOTE("'"), + DOUBLE_QUOTE("\""), + + FORWARD_SLASH("/"), + BACKWARD_SLASH("\\"), + + ZERO("0"), + + Y("Y"), + N("N"), + YES("Yes"), + NO("No"), + + TRUE_VALUE("true"), + FALSE_VALUE("false"); + + private final String value; + + GenericConstants(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + public static GenericConstants fromString(String parameterName) { + if (parameterName != null) { + for (GenericConstants objType: GenericConstants.values()) { + if (parameterName.equals(objType.value)) { + return objType; + } + } + } + + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/ListNode.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/ListNode.java new file mode 100644 index 0000000..ccd0ca8 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/ListNode.java @@ -0,0 +1,27 @@ +package com.shortthirdman.primekit.essentials.common; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ListNode { + + private int value; + + private ListNode next; + + public ListNode(int x) { + this.value = x; + this.next = null; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ListNode{"); + sb.append("value=").append(value); + sb.append(", next=").append(next); + sb.append('}'); + return sb.toString(); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/TreeNode.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/TreeNode.java new file mode 100644 index 0000000..4bd7755 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/TreeNode.java @@ -0,0 +1,40 @@ +package com.shortthirdman.primekit.essentials.common; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TreeNode { + + private int value; + private TreeNode leftNode; + private TreeNode rightNode; + + private TreeNode(int value) { + this.value = value; + } + + private TreeNode(int value, TreeNode leftNode, TreeNode rightNode) { + this.value = value; + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + public static TreeNode createTreeNode(int value) { + return new TreeNode(value); + } + + public static TreeNode newTreeNode(int value, TreeNode leftNode, TreeNode rightNode) { + return new TreeNode(value, leftNode, rightNode); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder("TreeNode{"); + builder.append("value=").append(value); + builder.append(", left=").append(leftNode.toString()); + builder.append(", right=").append(rightNode.toString()); + return builder.toString(); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/Converter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/Converter.java new file mode 100644 index 0000000..578888f --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/Converter.java @@ -0,0 +1,8 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; + +public interface Converter { + + Q convert(P input, @Nullable Object... options) throws Exception; +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToBooleanConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToBooleanConverter.java new file mode 100644 index 0000000..514161f --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToBooleanConverter.java @@ -0,0 +1,25 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +@Component +public class StringToBooleanConverter implements Converter { + + @Override + public Boolean convert(String input, @Nullable Object... options) throws Exception { + + if (input == null || StringUtils.isBlank(input)) { + throw new IllegalArgumentException("Input can not be null or empty"); + } + + if ("true".equalsIgnoreCase(input) || "T".equalsIgnoreCase(input)) { + return true; + } else if ("false".equalsIgnoreCase(input) || "F".equalsIgnoreCase(input)) { + return false; + } + + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDateConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDateConverter.java new file mode 100644 index 0000000..80e8add --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDateConverter.java @@ -0,0 +1,41 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.springframework.stereotype.Component; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Objects; + +@Component +public class StringToDateConverter implements Converter { + + private static final String ISO_DATE_FORMAT = ""; + + @Override + public Date convert(String input, @Nullable Object... options) throws Exception { + Date result = null; + String format = ISO_DATE_FORMAT; + Locale locale = Locale.ENGLISH; + + if (Objects.nonNull(options)) { + format = options.length > 1 ? String.valueOf(options[0]) : ISO_DATE_FORMAT; + locale = options.length > 1 ? (Locale) options[1] : Locale.ENGLISH; + } + + DateFormat fmt = new SimpleDateFormat(format, locale); + + try { + if (!input.isEmpty()) { + result = fmt.parse(input); + } + } catch (ParseException pe) { + throw pe; + } + + return result; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDoubleConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDoubleConverter.java new file mode 100644 index 0000000..42eee51 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToDoubleConverter.java @@ -0,0 +1,13 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class StringToDoubleConverter implements Converter { + + @Override + public Double convert(String input, @Nullable Object... options) throws Exception { + return 0.0; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToHashMapConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToHashMapConverter.java new file mode 100644 index 0000000..1c5e50a --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToHashMapConverter.java @@ -0,0 +1,15 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.springframework.stereotype.Component; + +import java.util.HashMap; + +@Component +public class StringToHashMapConverter implements Converter> { + + @Override + public HashMap convert(String input, @Nullable Object... options) throws Exception { + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToIntegerConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToIntegerConverter.java new file mode 100644 index 0000000..8a36e0f --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToIntegerConverter.java @@ -0,0 +1,26 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +@Component +public class StringToIntegerConverter implements Converter { + + @Override + public Integer convert(String input, @Nullable Object... options) throws Exception { + + Integer result = null; + + try { + if (Objects.nonNull(input) && !input.isBlank()) { + result = Integer.parseInt(input); + } + } catch (NumberFormatException nfe) { + throw nfe; + } + + return result; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToListConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToListConverter.java new file mode 100644 index 0000000..ce4bef7 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToListConverter.java @@ -0,0 +1,28 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +@Component +public class StringToListConverter implements Converter> { + + @Override + public List convert(String input, @Nullable Object... options) throws Exception { + + String delimiter = ""; + List result = List.of(); + + if (Objects.nonNull(options)) { + delimiter = options.length == 1 ? String.valueOf(options[0]) : ""; + } + + if (input == null) { + throw new IllegalArgumentException(""); + } + + return result; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToMapConverter.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToMapConverter.java new file mode 100644 index 0000000..5f5aa68 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/converter/StringToMapConverter.java @@ -0,0 +1,13 @@ +package com.shortthirdman.primekit.essentials.common.converter; + +import jakarta.annotation.Nullable; + +import java.util.Map; + +public class StringToMapConverter implements Converter> { + + @Override + public Map convert(String input, @Nullable Object... options) throws Exception { + return Map.of(); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/CryptoModes.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/CryptoModes.java new file mode 100644 index 0000000..988f8c7 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/CryptoModes.java @@ -0,0 +1,45 @@ +package com.shortthirdman.primekit.essentials.common.enums; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.Optional; + +@Getter +public enum CryptoModes { + + ECB("ECB", "Electronic Code Book Mode"), + CBC("CBC", "Cipher Block Chain Mode"), + CCM("CCM", "Counter/CBC Mode"), + CFB("CFB","Cipher Feedback Mode"), + OFB("OFB","Output Feedback Mode"), + CTR("CTR","Counter Mode"), + GCM("GCM","Galois/Counter Mode"), + KW("KW","Key Wrap Mode"), + KWP("KWP","Key Wrap Padding Mode"), + PCBC("PCBC","Propagating Cipher Block Chaining"); + + private final String modeName; + private final String qualifiedName; + + CryptoModes(String mode, String name) { + this.modeName = mode; + this.qualifiedName = name; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(modeName).append("=").append(qualifiedName); + return sb.toString(); + } + + public static CryptoModes fromMode(final String mode) { + if (mode != null) { + Optional obj = Arrays.stream(CryptoModes.values()).filter(cm -> cm.modeName.equals(mode)).findAny(); + return obj.orElse(null); + } + + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/FileTypes.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/FileTypes.java new file mode 100644 index 0000000..e00c514 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/FileTypes.java @@ -0,0 +1,35 @@ +package com.shortthirdman.primekit.essentials.common.enums; + +import lombok.Getter; + +@Getter +public enum FileTypes { + + DOCX("docx"), + DOC("doc"), + PDF("pdf"), + JPEG("jpeg"), + PNG("png"), + GIF("gif"), + CSV("csv"), + JSON("json"), + TXT("txt"), + RTF("rtf"); + + private final String extension; + + FileTypes(String extension) { + this.extension = extension; + } + + public static FileTypes fromString(String parameterName) { + if (parameterName != null) { + for (FileTypes objType: FileTypes.values()) { + if (parameterName.equals(objType.extension)) { + return objType; + } + } + } + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/HibernateDialects.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/HibernateDialects.java new file mode 100644 index 0000000..11487cd --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/enums/HibernateDialects.java @@ -0,0 +1,77 @@ +package com.shortthirdman.primekit.essentials.common.enums; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; + +@Getter +public enum HibernateDialects { + + ORACLE("oracle", "org.hibernate.dialect.OracleDialect"), + ORACLE_9I("oracle9i", "org.hibernate.dialect.Oracle9iDialect"), + ORACLE_10G("oracle10g", "org.hibernate.dialect.Oracle10gDialect"), + MYSQL("mysql", "org.hibernate.dialect.MySQLDialect"), + MYSQL_INNODB("mysqlinnodb", "org.hibernate.dialect.MySQLInnoDBDialect"), + MYSQL_MYISAM("mysqlmyisam", "org.hibernate.dialect.MySQLMyISAMDialect"), + DB2("db2","org.hibernate.dialect.DB2Dialect"), + DB2AS400("db2as400","org.hibernate.dialect.DB2400Dialect"), + DB2OS390("db2os390","org.hibernate.dialect.DB2390Dialect"), + MSSQL_SERVER("mssql","org.hibernate.dialect.SQLServerDialect"), + SYBASE("sybase","org.hibernate.dialect.SybaseDialect"), + SYBASE_ANYWHERE("sybaseanywhere","org.hibernate.dialect.SybaseAnywhereDialect"), + POSTGRESQL("postgres","org.hibernate.dialect.PostgreSQLDialect"), + SAP_DB("sap","org.hibernate.dialect.SAPDBDialect"), + INFORMIX("informix","org.hibernate.dialect.InformixDialect"), + HYPERSONIC_SQL("hypersonic","org.hibernate.dialect.HSQLDialect"), + INGRES("ingres","org.hibernate.dialect.IngresDialect"), + PROGRESS("progress","org.hibernate.dialect.ProgressDialect"), + MCKOI_SQL("mckoi","org.hibernate.dialect.MckoiDialect"), + INTERBASE("interbase","org.hibernate.dialect.InterbaseDialect"), + POINT_BASE("pointbase","org.hibernate.dialect.PointbaseDialect"), + FRONT_BASE("frontbase","org.hibernate.dialect.FrontbaseDialect"), + FIREBIRD("firebird","org.hibernate.dialect.FirebirdDialect"); + + private final String vendorName; + private final String driverClassName; + + HibernateDialects(String vendorName, String driverClassName) { + this.vendorName = vendorName; + this.driverClassName = driverClassName; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Vendor=").append(vendorName).append(", Driver Class=").append(driverClassName); + return sb.toString(); + } + + /** + * @param driverClass + * @return + */ + public static HibernateDialects fromDriverClass(String driverClass) { + if (driverClass != null) { + for (HibernateDialects obj : HibernateDialects.values()) { + if (driverClass.equals(obj.driverClassName)) { + return obj; + } + } + } + return null; + } + + public static HibernateDialects fromVendor(String vendor) { + if (vendor != null) { + Optional firstMatch = Arrays.stream(HibernateDialects.values()) + .sequential() + .filter(obj -> obj.vendorName.equals(vendor)) + .findAny(); + return firstMatch.orElse(null); + } + + return null; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ArrayUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ArrayUtils.java new file mode 100644 index 0000000..012a416 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ArrayUtils.java @@ -0,0 +1,125 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.util.Arrays; + +/** + * Utility class for Array methods + * + * @author shortthirdman-org + * @verison 1.0.0 + * @since 1.0.0 + */ +public final class ArrayUtils { + + private ArrayUtils() { + } + + /** + * Rotate an array of objects by an provided degree + * + * @param array the input array of object + * @param amount the amount to rotate + * @return the rotated array + */ + public static Object[] rotateArray(Object[] array, int amount) { + if (array == null || amount < 0) { + throw new IllegalArgumentException("Input array or rotation amount can not be null"); + } + + Object[] arr = Arrays.copyOf(array, array.length); + + for (int i = 0; i < amount; i++) { + for (int j = arr.length - 1; j > 0; j--) { + Object temp = arr[j]; + arr[j] = arr[j - 1]; + arr[j - 1] = temp; + } + } + return arr; + } + + /** + * Rotate a floating-point array by an provided degree + * + * @param array the input array of floating-point numbers + * @param amount the amount to rotate + * @return the rotated array + */ + public static float[] rotateArray(float[] array, int amount) { + if (array == null || amount < 0) { + throw new IllegalArgumentException("Input array or rotation amount can not be null"); + } + + float[] arr = Arrays.copyOf(array, array.length); + + for (int i = 0; i < amount; i++) { + for (int j = arr.length - 1; j > 0; j--) { + float temp = arr[j]; + arr[j] = arr[j - 1]; + arr[j - 1] = temp; + } + } + return arr; + } + + /** + * Rotate an array of objects by a provided range + * + * @param array the input array of objects + * @param from the starting index of range + * @param to the ending index of range + * @param n the number of elements + * @param the parameterized data-type + */ + public static void rotateArrayRange(T[] array, int from, int to, int n) { + rotateArrayRange(array, from, to, n, Arrays.copyOfRange(array, from, to)); + } + + /** + * Rotate an array of objects by a provided range by creating copy of range + * + * @param array the input array of objects + * @param from the starting index of range + * @param to the ending index of range + * @param n the number of elements + * @param copyOfRange the copy of range of elements + * @param the parameterized data-type + */ + public static void rotateArrayRange(T[] array, int from, int to, int n, T[] copyOfRange) { + final int d = to - from; + if (n < 0) + n = to - from + n; + for (int i = 0; i < d; ++i) + array[from + i] = copyOfRange[(i + n) % d]; + } + + /** + * Rotate an array of integers by a provided range + * + * @param array the input array of integer + * @param from the starting index of range + * @param to the ending index of range + * @param n the number of elements + */ + public static void rotateArrayRange(int[] array, int from, int to, int n) { + rotateArrayRange(array, from, to, n, Arrays.copyOfRange(array, from, to)); + } + + /** + * Rotate an array of integers by a provided range + * + * @param array the input array of integer + * @param from the starting index of range + * @param to the ending index of range + * @param n the number of elements + * @param copyOfRange the copy of range of elements + */ + public static void rotateArrayRange(int[] array, int from, int to, int n, int[] copyOfRange) { + final int d = to - from; + if (n < 0) + n = to - from + n; + for (int i = 0; i < d; ++i) + array[from + i] = copyOfRange[(i + n) % d]; + } + +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/BinaryTreeUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/BinaryTreeUtils.java new file mode 100644 index 0000000..1760e9f --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/BinaryTreeUtils.java @@ -0,0 +1,830 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import com.shortthirdman.primekit.essentials.common.CounterNode; +import com.shortthirdman.primekit.essentials.common.ListNode; +import com.shortthirdman.primekit.essentials.common.TreeNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Stack; +import java.util.TreeMap; + +public final class BinaryTreeUtils { + + private static ListNode h; + + private BinaryTreeUtils() { + } + + /** + * Post-order traversal of a binary tree + * @param root the root node + * @return + */ + public static List postorderTraversal(TreeNode root) { + + List list = new ArrayList<>(); + + if (root == null) { + return list; + } + + Stack stack = new Stack<>(); + stack.push(root); + + TreeNode prev = null; + while (!stack.empty()) { + TreeNode curr = stack.peek(); + + if (prev == null || prev.getLeftNode() == curr || prev.getRightNode() == curr) { + if (curr.getLeftNode() != null) { + stack.push(curr.getLeftNode()); + } else if (curr.getRightNode() != null) { + stack.push(curr.getRightNode()); + } else { + stack.pop(); + list.add(curr.getValue()); + } + } else if (curr.getLeftNode() == prev) { + if (curr.getRightNode() != null) { + stack.push(curr.getRightNode()); + } else { + stack.pop(); + list.add(curr.getValue()); + } + } else if (curr.getRightNode() == prev) { + stack.pop(); + list.add(curr.getValue()); + } + + prev = curr; + } + + return list; + } + + /** + * Pre-order traversal of a binary tree + * @param root the root node + * @return + */ + public static List preorderTraversal(TreeNode root) { + ArrayList list = new ArrayList<>(); + + if (root == null) { + return list; + } + + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.empty()) { + TreeNode n = stack.pop(); + list.add(n.getValue()); + + if (n.getRightNode() != null) { + stack.push(n.getRightNode()); + } + + if (n.getLeftNode() != null) { + stack.push(n.getLeftNode()); + } + } + + return list; + } + + /** + * In-order traversal of binary tree + * @param root the root node of binary tree + * @return + */ + public static List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + Stack stack = new Stack<>(); + + TreeNode p = root; + while (p != null) { + stack.push(p); + p = p.getLeftNode(); + } + + while (!stack.isEmpty()) { + TreeNode t = stack.pop(); + result.add(t.getValue()); + + t = t.getRightNode(); + while (t != null) { + stack.push(t); + t = t.getLeftNode(); + } + } + + return result; + } + + /** + * Given a binary tree, return the bottom-up level order traversal of its nodes' values + * @param root the root node of binary tree + * @return + */ + public static List> bottomUplevelOrderTraversal(TreeNode root) { + List> result = new ArrayList<>(); + + if(root == null){ + return result; + } + + LinkedList current = new LinkedList<>(); + LinkedList next = new LinkedList<>(); + current.offer(root); + + ArrayList numberList = new ArrayList<>(); + + while (!current.isEmpty()) { + TreeNode head = current.poll(); + + numberList.add(head.getValue()); + + if (head.getLeftNode() != null) { + next.offer(head.getLeftNode()); + } + + if (head.getRightNode() != null) { + next.offer(head.getRightNode()); + } + + if (current.isEmpty()) { + current = next; + next = new LinkedList<>(); + result.add(numberList); + numberList = new ArrayList<>(); + } + } + + List> reversedResult = new ArrayList<>(); + + for (int i = result.size() - 1; i >= 0; i--) { + reversedResult.add(result.get(i)); + } + + return reversedResult; + } + + /** + * Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). + * @param root the root node of binary tree + * @return + */ + public static List> levelOrderTraversal(TreeNode root) { + List> result = new ArrayList<>(); + + if (root == null) { + return result; + } + + LinkedList nodeQueue = new LinkedList<>(); + LinkedList levelQueue = new LinkedList<>(); + + nodeQueue.offer(root); + levelQueue.offer(1); + + while (!nodeQueue.isEmpty()) { + TreeNode node = nodeQueue.poll(); + Integer level = levelQueue.poll(); + var levelValue = level != null ? level : 0; + + List list; + + if (result.size() < levelValue) { + list = new ArrayList<>(); + result.add(list); + } else { + list = result.get(level - 1); + } + + list.add(node.getValue()); + + if (node.getLeftNode() != null) { + nodeQueue.offer(node.getLeftNode()); + levelQueue.offer(level + 1); + } + + if (node.getRightNode() != null) { + nodeQueue.offer(node.getRightNode()); + levelQueue.offer(level + 1); + } + } + + return result; + } + + /** + * Given a binary tree, return the vertical order traversal of its nodes' values. (ie, from top to bottom, column by column). + * @param root + * @return + */ + public static List> verticalOrderTraversal(TreeNode root) { + TreeMap> map = new TreeMap<>(); + + if (root == null) { + return null; + } + + LinkedList q1 = new LinkedList<>(); + LinkedList q2 = new LinkedList<>(); + q1.offer(root); + q2.offer(0); + + while (!q1.isEmpty()) { + TreeNode node = q1.poll(); + Integer order = q2.poll(); + + //add to map + ArrayList list = map.computeIfAbsent(order, k -> new ArrayList<>()); + list.add(node.getValue()); + + if (node.getLeftNode() != null) { + q1.offer(node.getLeftNode()); + q2.offer(order - 1); + } + + if (node.getRightNode() != null) { + q1.offer(node.getRightNode()); + q2.offer(order + 1); + } + } + + List> result = new ArrayList<>(map.values()); + + return result; + } + + /** + * @see @verticalOrderTraversal + * @param root + * @return + */ + public static List> verticalOrder(TreeNode root) { + List> result = new ArrayList<>(); + if (root == null) { + return result; + } + + int[] mm = new int[2]; + getMinMax(root, mm, 0); + + int len = mm[1] - mm[0] + 1; + + for (int i = 0; i < len; i++) { + result.add(new ArrayList<>()); + } + + LinkedList q1 = new LinkedList<>(); + LinkedList q2 = new LinkedList<>(); + + q1.offer(root); + q2.offer(0); + + while (!q1.isEmpty()) { + TreeNode h = q1.poll(); + int order = q2.poll(); + + result.get(order - mm[0]).add(h.getValue()); + + if (h.getLeftNode() != null) { + q1.offer(h.getLeftNode()); + q2.offer(order - 1); + } + + if (h.getRightNode() != null) { + q1.offer(h.getRightNode()); + q2.offer(order + 1); + } + } + + return result; + } + + private static void getMinMax(TreeNode node, int[] mm, int order){ + if (node == null) { + return; + } + + mm[0] = Math.min(mm[0], order); + mm[1] = Math.max(mm[1], order); + + getMinMax(node.getLeftNode(), mm, order - 1); + getMinMax(node.getRightNode(), mm, order + 1); + } + + /** + * Builds a binary tree + * @param inorder the in-order array + * @param postorder the post-order array + * @return tree node + */ + public static TreeNode buildTree(int[] inorder, int[] postorder) throws Exception { + if (Objects.isNull(inorder) || Objects.isNull(postorder)) { + throw new Exception("In-order or post-order arrays are null or empty"); + } + int inStart = 0; + int inEnd = inorder.length - 1; + int postStart = 0; + int postEnd = postorder.length - 1; + + return buildTree(inorder, inStart, inEnd, postorder, postStart, postEnd); + } + + /** + * Builds a binary tree + * @param inorder the in-order array + * @param inStart start index of in-order + * @param inEnd end index of in-order + * @param postorder the post-order array + * @param postStart start index of post-order + * @param postEnd end index of post-order + * @return tree node + */ + public static TreeNode buildTree(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) throws Exception { + if (inStart > inEnd || postStart > postEnd) { + return null; + } + + int rootValue = postorder[postEnd]; + TreeNode root = TreeNode.createTreeNode(rootValue); + + int k = 0; + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == rootValue) { + k = i; + break; + } + } + + root.setLeftNode(buildTree(inorder, inStart, k - 1, postorder, postStart, postStart + k - (inStart + 1))); + root.setRightNode(buildTree(inorder, k + 1, inEnd, postorder, postStart + k- inStart, postEnd - 1)); + + return root; + } + + /** + * Given a binary tree, collect a tree's nodes. Collect and remove all leaves, repeat until the tree is empty. + * @param root the root node of binary tree + * @return + */ + public static List> findLeaves(TreeNode root) { + HashMap map = new HashMap<>(); + getOrder(root, map); + + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + HashMap> reversed = new HashMap<>(); + + for (Map.Entry entry : map.entrySet()) { + min = Math.min(min, entry.getValue()); + max = Math.max(max, entry.getValue()); + + HashSet set = reversed.getOrDefault(entry.getValue(), new HashSet<>()); + set.add(entry.getKey()); + reversed.put(entry.getValue(), set); + } + + + List> result = new ArrayList<>(); + for (int i = min; i <= max; i++) { + HashSet set = reversed.get(i); + ArrayList l = new ArrayList<>(); + for (TreeNode td : set) { + l.add(td.getValue()); + } + result.add(l); + } + + return result; + } + + private static int getOrder(TreeNode root, HashMap map){ + if (root == null) { + return 0; + } + + int left = getOrder(root.getLeftNode(), map); + int right = getOrder(root.getRightNode(), map); + + int order = Math.max(left, right) + 1; + map.put(root, order); + + return order; + } + + /** + * Given a binary tree, find the maximum path sum. The path may start and end at any node in the tree. + * @param root the root node of binary tree + * @return the maximum path sum + */ + public static int maxPathSum(TreeNode root) { + int[] max = new int[1]; + max[0] = Integer.MIN_VALUE; + calculateSum(root, max); + return max[0]; + } + + public static int calculateSum(TreeNode root, int[] max) { + if (root == null) { + return 0; + } + + int left = calculateSum(root.getLeftNode(), max); + int right = calculateSum(root.getRightNode(), max); + + int current = Math.max(root.getValue(), Math.max(root.getValue() + left, root.getValue() + right)); + + max[0] = Math.max(max[0], Math.max(current, left + root.getValue() + right)); + + return current; + } + + /** + * @param root the root node of binary tree + * @return true, if tree is height-balanced + */ + public static boolean isBalanced(TreeNode root) { + if (root == null) + return true; + + return getHeight(root) != -1; + } + + public static int getHeight(TreeNode root) { + if (root == null) + return 0; + + int left = getHeight(root.getLeftNode()); + int right = getHeight(root.getRightNode()); + + if (left == -1 || right == -1) + return -1; + + if (Math.abs(left - right) > 1) { + return -1; + } + + return Math.max(left, right) + 1; + + } + + /** + * Given a binary tree, find its maximum depth.
+ * The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. + * @param root the root node os bincry tree + * @return the maximum depth + */ + public static int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + int leftDepth = maxDepth(root.getLeftNode()); + int rightDepth = maxDepth(root.getRightNode()); + + int bigger = Math.max(leftDepth, rightDepth); + + return bigger + 1; + } + + /** + * Given a binary tree, find its minimum depth.
+ * The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. + * + * @param root the root node of binary tree + * @return minimum depth of binary tree + */ + public static int minDepth(TreeNode root) { + if (root == null) { + return 0; + } + + LinkedList nodes = new LinkedList<>(); + LinkedList counts = new LinkedList<>(); + + nodes.add(root); + counts.add(1); + + while (!nodes.isEmpty()) { + TreeNode curr = nodes.remove(); + int count = counts.remove(); + + if (curr.getLeftNode() == null && curr.getRightNode() == null) { + return count; + } + + if (curr.getLeftNode() != null) { + nodes.add(curr.getLeftNode()); + counts.add(count + 1); + } + + if (curr.getRightNode() != null) { + nodes.add(curr.getRightNode()); + counts.add(count + 1); + } + } + + return 0; + } + + /** + * Given a binary tree, return all root-to-leaf paths. + * + * @param root the root node of binary tree + * @return the list of all binary tree path + */ + public static List binaryTreePaths(TreeNode root) { + ArrayList finalResult = new ArrayList<>(); + + if (root == null) { + return finalResult; + } + + ArrayList curr = new ArrayList<>(); + ArrayList> results = new ArrayList<>(); + + dfs(root, results, curr); + + for (ArrayList al : results) { + StringBuilder sb = new StringBuilder(); + sb.append(al.get(0)); + for (int i = 1; i < al.size(); i++) { + sb.append("->").append(al.get(i)); + } + + finalResult.add(sb.toString()); + } + + return finalResult; + } + + private static void dfs(TreeNode root, ArrayList> list, ArrayList curr){ + curr.add(String.valueOf(root.getValue())); + + if (root.getLeftNode() == null && root.getRightNode() == null) { + list.add(curr); + return; + } + + if (root.getLeftNode() != null) { + ArrayList temp = new ArrayList<>(curr); + dfs(root.getLeftNode(), list, temp); + } + + if (root.getRightNode() != null) { + ArrayList temp = new ArrayList<>(curr); + dfs(root.getRightNode(), list, temp); + } + } + + /** + * @param root the root node of binary tree + * @param p the left node + * @param q the right node + * @return the lowest common ancestor node + */ + public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + CounterNode n = buildCounterNode(root, p, q); + return n.getNode(); + } + + private static CounterNode buildCounterNode(TreeNode root, TreeNode p, TreeNode q){ + if (root == null) { + return new CounterNode(null, 0); + } + + CounterNode left = buildCounterNode(root.getLeftNode(), p, q); + if (left.getCount() == 2) { + return left; + } + + CounterNode right = buildCounterNode(root.getRightNode(), p, q); + if (right.getCount() == 2) { + return right; + } + + int c = left.getCount() + right.getCount() + (root == p ? 1 : 0) + (root == q ? 1 : 0); + + return new CounterNode(root, c); + } + + /** + * @param head + * @return + */ + public static ListNode deleteDuplicates(ListNode head) { + if (head == null || head.getNext() == null) { + return head; + } + + ListNode prev = head; + ListNode p = head.getNext(); + + while (p != null) { + if (p.getValue() == prev.getValue()) { + prev.setNext(p.getNext()); + } else { + prev = p; + } + p = p.getNext(); + } + + return head; + } + + /** + * Given a binary tree, flatten it to a linked list in-place. + * @param root the root node of binary tree + */ + public void flattenTree(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode p = root; + + while (p != null || !stack.empty()) { + if (p!= null) { + if (p.getRightNode() != null) { + stack.push(p.getRightNode()); + } + + if (p.getLeftNode() != null) { + p.setRightNode(p.getLeftNode()); + p.setLeftNode(null); + } else if (!stack.empty()) { + TreeNode temp = stack.pop(); + p.setRightNode(temp); + } + p = p.getRightNode(); + } + } + } + + /** + * @param head + * @return + */ + public static TreeNode sortedListToBST(ListNode head) { + if (head == null) + return null; + + h = head; + int len = getLength(head); + return buildTreeBottomUp(0, len - 1); + } + + private static int getLength(ListNode head) { + int len = 0; + ListNode p = head; + + while (p != null) { + len++; + p = p.getNext(); + } + return len; + } + + private static TreeNode buildTreeBottomUp(int start, int end) { + if (start > end) { + return null; + } + + int mid = (start + end) / 2; + + TreeNode left = buildTreeBottomUp(start, mid - 1); + TreeNode root = TreeNode.createTreeNode(h.getValue()); + h = h.getNext(); + TreeNode right = buildTreeBottomUp(mid + 1, end); + + root.setLeftNode(left); + root.setRightNode(right); + + return root; + } + + /** + * Given a non-empty binary search tree and a target value, find the value in the BST that is closest to the target.
+ * Recursively traverse down the root. When target is less than root, go left; when target is greater than root, go right. + * + * @param rootNode the root node of binary tree + * @param target the target value to search for + * @return the closest value to target + */ + public static int closestValue(TreeNode rootNode, double target) { + double min = Double.MAX_VALUE; + int result = rootNode.getValue(); + + while (rootNode != null) { + if (target > rootNode.getValue()) { + double diff = Math.abs(rootNode.getValue() - target); + + if (diff < min) { + min = Math.min(min, diff); + result = rootNode.getValue(); + } + + rootNode = rootNode.getRightNode(); + } else if (target < rootNode.getValue()) { + double diff = Math.abs(rootNode.getValue() - target); + + if (diff < min) { + min = Math.min(min, diff); + result = rootNode.getValue(); + } + + rootNode = rootNode.getLeftNode(); + } else { + return rootNode.getValue(); + } + } + + return result; + } + + public static boolean isValidBST(TreeNode root) { + return isValidBST(root, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + } + + public static boolean isValidBST(TreeNode p, double min, double max){ + if (p == null) { + return true; + } + + if (p.getValue() <= min || p.getValue() >= max) { + return false; + } + + return isValidBST(p.getLeftNode(), min, p.getValue()) && isValidBST(p.getRightNode(), p.getValue(), max); + } + + /** + * For a undirected graph with tree characteristics, we can choose any node as the root.
+ * The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs).
+ * + * Given such a graph, write a function to find all the MHTs and return a list of their root labels.
+ * The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels). + * + * @param n the number of the nodes + * @param edges the edges of the tree + * @return list of their root labels/edges + */ + public static List findMinHeightTrees(int n, int[][] edges) { + List result = new ArrayList<>(); + if (n == 0) { + return result; + } + + if (n == 1) { + result.add(0); + return result; + } + + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i < n; i++) { + graph.add(new HashSet<>()); + } + + for (int[] edge : edges) { + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + + LinkedList leaves = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (graph.get(i).size() == 1) { + leaves.offer(i); + } + } + + if (leaves.size() == 0) { + return result; + } + + while (n > 2) { + n = n - leaves.size(); + + LinkedList newLeaves = new LinkedList<>(); + + for (int l : leaves) { + int neighbor = graph.get(l).iterator().next(); + graph.get(neighbor).remove(l); + if (graph.get(neighbor).size() == 1) { + newLeaves.add(neighbor); + } + } + + leaves = newLeaves; + } + + return leaves; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ClassUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ClassUtils.java new file mode 100644 index 0000000..1149eb2 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ClassUtils.java @@ -0,0 +1,112 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +public final class ClassUtils { + + private ClassUtils() { + } + + /** + * Return a resource URL. + * BL: if this is command line operation, the classloading issues + * are more sane. During servlet execution, we explicitly set + * the ClassLoader. + * + * @return The context classloader. + * @throws MalformedURLException If a loading error occurs + */ + public static URL getResource(String resource) throws MalformedURLException { + return getClassLoader().getResource(resource); + } + + /** + * Return the context classloader. + * BL: if this is command line operation, the classloading issues + * are more sane. During servlet execution, we explicitly set + * the ClassLoader. + * + * @return The context classloader. + */ + public static ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + /** + * Load a given resource. + *

+ * This method will try to load the resource using the following methods (in order): + *

    + *
  • From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()} + *
  • From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()} + *
  • From the {@link Class#getClassLoader() callingClass.getClassLoader() } + *
+ * + * @param resourceName The name of the resource to load + * @param caller The Class object of the calling object + */ + public static URL getResource(String resourceName, Class caller) { + URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName); + + if (url == null) { + url = ClassUtils.class.getClassLoader().getResource(resourceName); + } + + if (url == null) { + url = caller.getClassLoader().getResource(resourceName); + } + + return url; + } + + /** + * This is a convenience method to load a resource as a stream. + *

+ * The algorithm used to find the resource is given in getResource() + * + * @param resourceName The name of the resource to load + * @param caller The Class object of the calling object + */ + public static InputStream getResourceAsStream(String resourceName, Class caller) { + URL url = getResource(resourceName, caller); + try { + return (url != null) ? url.openStream() : null; + } catch (IOException e) { + return null; + } + } + + /** + * Load a class with a given name. + *

+ * It will try to load the class in the following order: + *

    + *
  • From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()} + *
  • Using the basic {@link Class#forName(java.lang.String) } + *
  • From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()} + *
  • From the {@link Class#getClassLoader() callingClass.getClassLoader() } + *
+ * + * @param className The name of the class to load + * @param caller The Class object of the calling object + * @throws ClassNotFoundException If the class cannot be found anywhere. + */ + public static Class loadClass(String className, Class caller) throws ClassNotFoundException { + try { + return Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + try { + return Class.forName(className); + } catch (ClassNotFoundException ex) { + try { + return ClassUtils.class.getClassLoader().loadClass(className); + } catch (ClassNotFoundException cnfe) { + return caller.getClassLoader().loadClass(className); + } + } + } + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CollectionUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CollectionUtils.java new file mode 100644 index 0000000..b4ee147 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CollectionUtils.java @@ -0,0 +1,135 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public final class CollectionUtils { + + private CollectionUtils() { + } + + /** + * Converts a array of elements into list + * + * @param array the typed array + * @return list of elements + */ + public static List toList(T[] array) { + return Arrays.stream(array).collect(Collectors.toList()); + } + + /** + * Converts a array of elements into set + * + * @param array the typed array + * @return set of elements + */ + public static Set toSet(T[] array) { + return Arrays.stream(array).collect(Collectors.toSet()); + } + + /** + * @param array the typed array + * @return + */ + public static Set toSet(T[] array, boolean preserveOrder) { + if (preserveOrder) { + return Arrays.stream(array) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + return Arrays.stream(array).collect(Collectors.toSet()); + } + + /** + * @param collection + * @param preserveOrder + * @param + * @return + */ + public static Set toSet(Collection collection, boolean preserveOrder) { + if (preserveOrder) { + return new LinkedHashSet<>(collection); + } + return new HashSet<>(collection); + } + + /** + * @param array the array of items + * @param predicate the filter predicate condition + * @param the typed-data of items in collection + * @return the filtered collection of elements + */ + public static List filterArray(T[] array, Predicate predicate) { + return Arrays.stream(array) + .filter(predicate) + .collect(Collectors.toCollection(ArrayList::new)); + } + + /** + * @param collection the collection of items + * @param predicate the filter predicate condition + * @param the typed-data of items in collection + * @return the filtered collection of items + */ + public static List filterCollection(Collection collection, Predicate predicate) { + return collection.stream().filter(predicate) + .collect(Collectors.toCollection(ArrayList::new)); + } + + /** + * Checks if the list is valid - whether the list is empty or size is zero + * + * @param list the list of object to be checked + * @return boolean value + */ + public static Boolean isListValid(List list) { + if (list != null) { + if (list.isEmpty()) { + return Boolean.FALSE; + } else { + return Boolean.TRUE; + } + } else { + return Boolean.FALSE; + } + } + + /** + * @param keyExtractors the key extractors function + * @param generic type of the predicate + * @return the unique pair from the input + */ + @SafeVarargs + public static Predicate distinctByKeys(Function... keyExtractors) { + final Map, Boolean> seen = new ConcurrentHashMap<>(); + + return t -> + { + final List keys = Arrays.stream(keyExtractors) + .map(ke -> ke.apply(t)) + .collect(Collectors.toList()); + + return seen.putIfAbsent(keys, Boolean.TRUE) == null; + }; + } + + /** + * @param map the input map of -pair + * @param the typed key + * @param the typed value + * @return the map of sorted by value + */ + public static > Map sortByValue(Map map) { + List> list = new LinkedList<>(map.entrySet()); + Collections.sort(list, (o1, o2) -> -(o1.getValue()).compareTo(o2.getValue())); + + Map result = new LinkedHashMap(); + for (Map.Entry entry : list) { + result.put(entry.getKey(), entry.getValue()); + } + return result; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ConfigurationUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ConfigurationUtils.java new file mode 100644 index 0000000..51f516d --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/ConfigurationUtils.java @@ -0,0 +1,92 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +public final class ConfigurationUtils { + + private ConfigurationUtils() { + } + + private static final Map propertiesConfigMap = new LinkedHashMap<>(); + + /** + * Loads all the configuration properties from the file name + * @param propertiesKey the file name key of properties configuration + * @return the properties list in pair + */ + public static Properties loadConfig(String propertiesKey) { + Properties config = null; + String fileName = propertiesKey; + if (!propertiesKey.endsWith(".properties")) { + fileName = propertiesKey + ".properties"; + } + try (InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { + if (Objects.isNull(input)) { + throw new IllegalStateException(propertiesKey + ".properties load failed, reason: "); + } + try (Reader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) { + config = new Properties(); + config.load(reader); + propertiesConfigMap.put(propertiesKey, config); + } catch (Exception e) { + throw new IllegalStateException(propertiesKey + ".properties error: " + e.getMessage()); + } + } catch (Exception e) { +// Logger.getGlobal().log(Level.WARNING, propertiesKey + ".properties load failed, reason:", e); + return config; + } + return config; + } + + /** + * @param fileKey the file name key of properties configuration + * @param key the key name + * @return the value of the key in the configuration file + */ + public static String getProperty(String fileKey, String key) { + Properties thisConfig = propertiesConfigMap.get(fileKey); + if (null == thisConfig) { + thisConfig = loadConfig(fileKey); + } + + if (null == thisConfig) { + return null; + } else { + return thisConfig.getProperty(key); + } + } + + /** + * @param fileKey the file name key of properties configuration + * @param key the key name + * @param defaultValue the default value if no key found + * @return the value of the key in the configuration file + */ + public static String getProperty(String fileKey, String key, String defaultValue) { + String result = getProperty(fileKey, key); + if (null == result) { + return defaultValue; + } else { + return result; + } + } + + /** + * @param fileKey the file name key of properties configuration + * @return the properties list in pair + */ + public static Properties getProperty(String fileKey) { + Properties thisConfig = propertiesConfigMap.get(fileKey); + if (null == thisConfig) { + thisConfig = loadConfig(fileKey); + } + return thisConfig; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CryptoUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CryptoUtils.java new file mode 100644 index 0000000..3e27934 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/CryptoUtils.java @@ -0,0 +1,512 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import org.apache.commons.lang3.StringUtils; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.xml.bind.DatatypeConverter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import java.util.Base64; +import java.util.Random; +import java.util.Set; +import java.util.TreeSet; + +public final class CryptoUtils { + + private static final Random RANDOM = new SecureRandom(); + private static final String ALPHA_NUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final int ITERATIONS = 10000; + private static final int KEY_LENGTH = 256; + + private CryptoUtils() { + } + + /** + * @return set of all cryptographic algorithms + */ + public static Set listAllAlgorithms() { + Set algos = new TreeSet<>(); + + for (Provider provider : Security.getProviders()) { + Set service = provider.getServices(); + service.stream().map(Provider.Service::getAlgorithm).forEach(algos::add); + } + + return algos; + } + + /** + * @param algorithm the algorithm name + * @return the block size + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + */ + public static int getBlockSize(final String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { + int size = 0; + + if (StringUtils.isBlank(algorithm)) { + return size; + } + + size = Cipher.getInstance(algorithm).getBlockSize(); + + return size; + } + + /** + * @param algorithm the algorithm to generate key for + * @param n the key length + * @return the secret key + * @throws NoSuchAlgorithmException + */ + public static SecretKey generateKey(final String algorithm, final int n) throws NoSuchAlgorithmException { + SecretKey key = null; + + if (algorithm == null || n <= 0) { + return key; + } + + KeyGenerator keygenerator = KeyGenerator.getInstance(algorithm); + keygenerator.init(n); + key = keygenerator.generateKey(); + + return key; + } + + public static IvParameterSpec generateIv() { + byte[] initializationVector = new byte[16]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(initializationVector); + return new IvParameterSpec(initializationVector); + } + + /** + * @param algorithm the Cipher algorithm + * @param input the input plain text to encrypt + * @param key the secret key + * @param ivParamSpec the IV parameter spec + * @return Encrypted content of the plain text + * @throws Exception + */ + public static byte[] encrypt(final String algorithm, final String input, final SecretKey key, IvParameterSpec ivParamSpec) throws Exception { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec); + return cipher.doFinal(input.getBytes(StandardCharsets.UTF_8)); + } + + /** + * @param algorithm the Cipher algorithm + * @param cipherText the input cipher text to decrypt + * @param key the secret key + * @param ivParamSpec the IV parameter spec + * @return Decrypted content of the cipher encrypted text + * @throws Exception + */ + public static String decrypt(final String algorithm, final byte[] cipherText, final SecretKey key, final IvParameterSpec ivParamSpec) throws Exception { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec); + byte[] plainText = cipher.doFinal(cipherText); + return new String(plainText); + } + + /** + * @return + * @throws Exception + */ + public static KeyPair generateRSAKKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(3072); + return keyPairGenerator.generateKeyPair(); + } + + public static String decrypt(byte[] cipherText, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(cipherText); + return new String(result); + } + + public static byte[] encrypt(String plainText, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + } + + public static byte[] generateDigitalSignature(byte[] plainText, PrivateKey privateKey) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] messageHash = md.digest(plainText); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(messageHash); + } + + public static boolean verify(byte[] plainText, byte[] digitalSignature, PublicKey publicKey) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hashedMessage = md.digest(plainText); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] decryptedMessageHash = cipher.doFinal(digitalSignature); + + return Arrays.equals(decryptedMessageHash, hashedMessage); + } + + /** + * + * @param secretKey + * Key used to encrypt data + * @param plainText + * Text input to be encrypted + * @return Returns encrypted text + * + */ + public static String encrypt(String secretKey, String plainText, byte[] salt, int keyCount) + throws NoSuchAlgorithmException, InvalidKeySpecException, + NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, + IllegalBlockSizeException, BadPaddingException { + KeySpec keySpec = new PBEKeySpec(secretKey.toCharArray(), salt, keyCount); + SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec); + + AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, keyCount); + + Cipher ecipher = Cipher.getInstance(key.getAlgorithm()); + ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); + byte[] in = plainText.getBytes(StandardCharsets.UTF_8); + byte[] out = ecipher.doFinal(in); + return DatatypeConverter.printBase64Binary(out); + } + + /** + * @param secretKey Key used to encrypt data + * @param encryptedText Text input to be encrypted + * @return Returns decrypted text + */ + public static String decrypt(String secretKey, String encryptedText, byte[] salt, int keyCount) throws NoSuchAlgorithmException, + NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException { + KeySpec keySpec = new PBEKeySpec(secretKey.toCharArray(), salt, keyCount); + SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec); + + AlgorithmParameterSpec algoParamSpec = new PBEParameterSpec(salt, keyCount); + + Cipher dcipher = Cipher.getInstance(key.getAlgorithm()); + dcipher.init(2, key, algoParamSpec); + byte[] enc = DatatypeConverter.parseBase64Binary(encryptedText); + byte[] utf8 = dcipher.doFinal(enc); + return new String(utf8, StandardCharsets.UTF_8); + } + + /** + * @param algorithm the algorithm name + * @param key the secret key + * @param iv the IV parameter secret key + * @param inputFile the input file + * @param outputFile the output encrypted file + * @throws IOException + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + */ + public static void encryptFile(String algorithm, SecretKey key, IvParameterSpec iv, + File inputFile, File outputFile) throws IOException, NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + FileInputStream inputStream = new FileInputStream(inputFile); + FileOutputStream outputStream = new FileOutputStream(outputFile); + byte[] buffer = new byte[64]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + byte[] output = cipher.update(buffer, 0, bytesRead); + if (output != null) { + outputStream.write(output); + } + } + + byte[] outputBytes = cipher.doFinal(); + + if (outputBytes != null) { + outputStream.write(outputBytes); + } + + inputStream.close(); + outputStream.close(); + } + + /** + * Encrypt the given serialized object with the specified algorithm + * @param algorithm the algorithm name + * @param object the object to encrypt + * @param key the secret key + * @param iv the IV parameter key + * @return + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + * @throws IOException + * @throws IllegalBlockSizeException + */ + public static SealedObject encryptObject(String algorithm, Serializable object, + SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, + InvalidKeyException, IOException, IllegalBlockSizeException { + SealedObject sealedObject = null; + + if (algorithm != null && key != null && iv != null) { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + sealedObject = new SealedObject(object, cipher); + } + + return sealedObject; + } + + /** + * @param algorithm + * @param sealedObject + * @param key + * @param iv + * @return + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + * @throws ClassNotFoundException + * @throws BadPaddingException + * @throws IllegalBlockSizeException + * @throws IOException + */ + public static Serializable decryptObject(String algorithm, SealedObject sealedObject, + SecretKey key, IvParameterSpec iv) throws NoSuchPaddingException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, + ClassNotFoundException, BadPaddingException, IllegalBlockSizeException, + IOException { + + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Serializable unsealObject = (Serializable) sealedObject.getObject(cipher); + return unsealObject; + } + + public static String getSalt(int length) { + StringBuilder returnValue = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + returnValue.append(ALPHA_NUMERIC.charAt(RANDOM.nextInt(ALPHA_NUMERIC.length()))); + } + + return new String(returnValue); + } + + public static byte[] hash(char[] password, byte[] salt) { + PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH); + + Arrays.fill(password, Character.MIN_VALUE); + + try { + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + return skf.generateSecret(spec).getEncoded(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new AssertionError("Error while hashing a password: " + e.getMessage(), e); + } finally { + spec.clearPassword(); + } + } + + public static String generateSecurePassword(String password, String salt) { + if (password == null || salt == null) { + throw new IllegalArgumentException(""); + } + String returnValue = null; + + byte[] securePassword = hash(password.toCharArray(), salt.getBytes()); + + returnValue = Base64.getEncoder().encodeToString(securePassword); + + return returnValue; + } + + public static boolean verifyUserPassword(String providedPassword, String securedPassword, String salt) { + boolean returnValue = false; + + // Generate New secure password with the same salt + String newSecurePassword = generateSecurePassword(providedPassword, salt); + + // Check if two passwords are equal + returnValue = newSecurePassword.equalsIgnoreCase(securedPassword); + + return returnValue; + } + + /** + * @param input the input text to get SHA hash + * @param algorithm the algorithm name + * @return the message digest + * @throws NoSuchAlgorithmException the exception for invalid algorithm + */ + public static byte[] getSHA(String input, String algorithm) throws NoSuchAlgorithmException { + if (input == null || algorithm == null) { + throw new NoSuchAlgorithmException(""); + } + + MessageDigest md = MessageDigest.getInstance(algorithm); + return md.digest(input.getBytes(StandardCharsets.UTF_8)); + } + + /** + * @param hash the hash to convert + * @return String + */ + public static String toHexString(byte[] hash) { + BigInteger number = new BigInteger(1, hash); + + StringBuilder hexString = new StringBuilder(number.toString(16)); + + while (hexString.length() < 32) { + hexString.insert(0, '0'); + } + + return hexString.toString(); + } + + /** + * @param digest + * @return String + */ + public static String toBase64String(byte[] digest) { + return Base64.getEncoder().encodeToString(digest); + } + + /** + * Loads a private key with the specified org name from a keystore. + * + * @param keyStore keystore from which to load the private key + * @param orgName name of the private key org + * @param password keystore password + * @return PrivateKey + */ + public static PrivateKey loadKey(KeyStore keyStore, String password, String orgName) { + try { + PrivateKey key = (PrivateKey) keyStore.getKey(orgName, password.toCharArray()); + if (key == null) { + throw new RuntimeException("Unable to get key for " + "name \"" + orgName + "\" using password \"" + + password + "\" from keystore"); + } + + return key; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Get a private key from the keystore by name and password. + * + * @param keystore keystore from which to load the private key + * @param alias name of the private key alias + * @param password keystore password + * @return Key + * @throws GeneralSecurityException + */ + public static Key getKey(KeyStore keystore, String alias, String password) throws GeneralSecurityException { + return keystore.getKey(alias, password.toCharArray()); + } + + /** + * Loads a private key from input stream + * + * @param inStream the input stream to load key + * @return Key + */ + public static Key getKey(InputStream inStream) { + try { + ObjectInputStream ois = new ObjectInputStream(inStream); + return (Key) ois.readObject(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Gets the public key from the encoded byte. + * The bytes can be recovered from a Hex string saved in a file etc. + * + * @param encodedKey the encoded public key in bytes + * @param kfAlgorithm the key factory algorithm name + * @return PublicKey + * @throws InvalidKeySpecException + * @throws NoSuchAlgorithmException + */ + public static PublicKey getPublicKey(byte[] encodedKey, String kfAlgorithm) throws InvalidKeySpecException, NoSuchAlgorithmException { + + if (kfAlgorithm == null || kfAlgorithm.isEmpty()) { + kfAlgorithm = "RSA"; + } + + X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedKey); + KeyFactory kf = KeyFactory.getInstance(kfAlgorithm); + + return kf.generatePublic(spec); + } + + /** + * @param modulus the modulus value of key + * @param exponent the exponent value of key + * @return RSAPublicKey + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + public static RSAPublicKey getPublicKey(BigInteger modulus, BigInteger exponent) throws NoSuchAlgorithmException, InvalidKeySpecException { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent); + + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java new file mode 100644 index 0000000..12edc91 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/DateUtils.java @@ -0,0 +1,395 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import org.apache.commons.lang3.StringUtils; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.ResolverStyle; +import java.time.format.TextStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +/** + * @apiNote Utility class for operations on {@link LocalDate}, {@link Date}, {@link LocalDateTime} + * @author shortthirdman + * @since 1.0 + * https://howtodoinjava.com/java/date-time/date-validation/ + */ +public final class DateUtils { + + private DateUtils() { + } + + /** + * Checks if the year is leap year or not + * @param year the calendar year + * @return true if calendar year is leap year, otherwise false + */ + public static boolean isLeapYear(int year) { + return (year % 100 != 0) || (year % 400 == 0); + } + + public static Date convertToDate(ZonedDateTime zonedDateTime) { + return Date.from(zonedDateTime.toInstant()); + } + + public static ZonedDateTime convertToZonedDateTime(Date date, ZoneId zone) { + return date.toInstant().atZone(zone); + } + + public static Timestamp convertToTimeStampFromInstant(ZonedDateTime zonedDateTime) { + Instant instant = zonedDateTime.toInstant(); + return Timestamp.from(instant); + } + + public static Timestamp convertToTimeStamp(ZonedDateTime zonedDateTime) { + LocalDateTime localDateTime = zonedDateTime.toLocalDateTime(); + return Timestamp.valueOf(localDateTime); + } + + public static ZonedDateTime convertToInstantZonedDateTime(Timestamp timestamp) { + Instant instant = timestamp.toInstant(); + return instant.atZone(ZoneId.systemDefault()); + } + + public static ZonedDateTime convertToCalendarZonedDateTime(Timestamp timestamp) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(timestamp.getTime()); + return calendar.toInstant().atZone(ZoneId.systemDefault()); + } + + public static ZonedDateTime convertToZonedDateTimeFromLocalDateTime(Timestamp timestamp) { + LocalDateTime localDateTime = timestamp.toLocalDateTime(); + return localDateTime.atZone(ZoneId.systemDefault()); + } + + /** + * Converts LocalDate to Date + * @param localDate the LocalDate to convert + * @return + */ + public static Date asDate(LocalDate localDate) { + return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts LocalDateTime as Date + * @param localDateTime the LocalDateTime to convert + * @return + */ + public static Date asDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + /** + * Converts Date to LocalDate + * @param date the Date to convert + * @return + */ + public static LocalDate asLocalDate(Date date) { + return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate(); + } + + /** + * Converts Date to LocalDateTime + * @param date the Date to convert + * @return + */ + public static LocalDateTime asLocalDateTime(Date date) { + return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime(); + } + + /** + * @param dateValue + * @return + */ + public static ZonedDateTime asZonedDateTime(String dateValue, String pattern) { + String defaultPattern = (pattern == null) ? "yyyy-MM-dd HH:mm:ss z": pattern; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(defaultPattern); + ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateValue, formatter); + return zonedDateTime; + } + + /** + * @param startDate the starting date + * @param endDate the ending date + * @return + */ + public static List getDatesBetween(LocalDate startDate, LocalDate endDate) { + if (startDate == null || endDate == null) { + return null; + } + + long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate); + return IntStream.iterate(0, i -> i + 1) + .limit(numOfDaysBetween) + .mapToObj(startDate::plusDays) + .collect(Collectors.toList()); + } + + /** + * Converts a date from source to target format in specified locale + * + * @param dateValue the date value to be formatted + * @param fromFmt the original source format + * @param toFmt the target destination format + * @param locale the locale, if not passed defaults to `Locale.ENGLISH` + * @return + * @throws ParseException + */ + public static String formatDate(String dateValue, String fromFmt, String toFmt, Locale locale) throws ParseException { + if (StringUtils.isBlank(dateValue) || StringUtils.isBlank(fromFmt) || StringUtils.isBlank(toFmt)) { + return null; + } + + if (Objects.isNull(locale)) { + locale = Locale.ENGLISH; + } + + SimpleDateFormat sdf = new SimpleDateFormat(fromFmt, locale); + Date d = sdf.parse(dateValue); + + sdf = new SimpleDateFormat(toFmt, locale); + + return sdf.format(d); + } + + /** + * Converts current date into required format + * + * @param format the target format + * @return + */ + public static String getCurrentDate(String format) { + if (StringUtils.isBlank(format)) { + return null; + } + + SimpleDateFormat formatter = new SimpleDateFormat(format); + Date date = new Date(); + return formatter.format(date); + } + + public static boolean isWeekend(final LocalDate localDate) { + DayOfWeek day = DayOfWeek.of(localDate.get(ChronoField.DAY_OF_WEEK)); + return day == DayOfWeek.SUNDAY || day == DayOfWeek.SATURDAY; + } + + public static boolean isWeekend(final Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + + int day = cal.get(Calendar.DAY_OF_WEEK); + return day == Calendar.SATURDAY || day == Calendar.SUNDAY; + } + + /** + * @param localDate the {@link LocalDate} to add days + * @param days the number of business days + * @param holidays the list of holidays + * @return the final business day + */ + public static LocalDate addBusinessDays(LocalDate localDate, int days, List holidays) { + if (localDate == null || days <= 0 || holidays.isEmpty()) { + throw new IllegalArgumentException("Invalid method argument(s) " + "to addBusinessDays(" + localDate + "," + days + "," + holidays + ")"); + } + + Predicate isHoliday = date -> !holidays.isEmpty() && holidays.contains(date); + + Predicate isWeekend = date + -> date.getDayOfWeek() == DayOfWeek.SATURDAY + || date.getDayOfWeek() == DayOfWeek.SUNDAY; + + LocalDate result = localDate; + while (days > 0) { + result = result.plusDays(1); + if (isHoliday.or(isWeekend).negate().test(result)) { + days--; + } + } + + return result; + } + + /** + * @param localDate the {@link LocalDate} to subtract days + * @param days the number of business days + * @param holidays the list of holidays + * @return the final business day + */ + public static LocalDate subtractBusinessDays(LocalDate localDate, int days, List holidays) { + if (localDate == null || days <= 0 || holidays == null) { + throw new IllegalArgumentException("Invalid method argument(s) " + + "to subtractBusinessDays(" + localDate + "," + days + "," + holidays + ")"); + } + + Predicate isHoliday = date -> !holidays.isEmpty() && holidays.contains(date); + + Predicate isWeekend = + date -> date.getDayOfWeek() == DayOfWeek.SATURDAY + || date.getDayOfWeek() == DayOfWeek.SUNDAY; + + LocalDate result = localDate; + while (days >= 0) { + result = result.minusDays(1); + if (isHoliday.or(isWeekend).negate().test(result)) { + days--; + } + } + + return result; + } + + /** + * @param start the start date + * @param end the end date + * @return the list of dates in between inclusive of both + */ + public static List datesBetween(LocalDate start, LocalDate end) { + if (start == null || end == null) { + throw new IllegalArgumentException("Invalid method argument(s) to datesBetween(" + start + "," + end + ")"); + } + + // https://howtodoinjava.com/java/date-time/dates-between-two-dates/ + + final List list = start.datesUntil(end) + .collect(Collectors.toList()); + + return list; + } + + public static List allBusinessDays(final LocalDate startDate, final LocalDate endDate, final List holidays) { + if (startDate == null || endDate == null) { + throw new IllegalArgumentException("Invalid method argument(s) to countBusinessDaysBetween (" + + startDate + "," + endDate + "," + holidays + ")"); + } + List businessDays = List.of(); + + // Predicate 1: Is a given date is a holiday + Predicate isHoliday = date -> !holidays.isEmpty() && holidays.contains(date); + + // Predicate 2: Is a given date is a weekday + Predicate isWeekend = date -> date.getDayOfWeek() == DayOfWeek.SATURDAY + || date.getDayOfWeek() == DayOfWeek.SUNDAY; + + // Iterate over stream of all dates and check each day against any weekday or holiday + businessDays = startDate.datesUntil(endDate) + .filter(isWeekend.or(isHoliday).negate()) + .collect(Collectors.toList()); + + return businessDays; + } + + /** + * @param startDate the start date + * @param endDate the end date + * @param holidays the list of holidays + * @return the list of dates + */ + @Deprecated(forRemoval = true, since = "1.0.0") + public static List businessDaysBetween(final LocalDate startDate, final LocalDate endDate, final List holidays) { + // Validate method arguments + if (startDate == null || endDate == null) { + throw new IllegalArgumentException("Invalid method argument(s) to countBusinessDaysBetween(" + startDate + + "," + endDate + "," + holidays + ")"); + } + + Predicate isHoliday = date -> !holidays.isEmpty() && holidays.contains(date); + + Predicate isWeekend = date -> date.getDayOfWeek() == DayOfWeek.SATURDAY + || date.getDayOfWeek() == DayOfWeek.SUNDAY; + + long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); + + return Stream.iterate(startDate, date -> date.plusDays(1)) + .limit(daysBetween) + .filter(isHoliday.or(isWeekend).negate()) + .collect(Collectors.toList()); + } + + /** + * @param dateValue the date value in {@link String} + * @param formatPattern the date format pattern + * @return the parsed {@link LocalDate} + */ + public static LocalDate strictParseDate(String dateValue, String formatPattern) { + LocalDate parsedDate = null; + + if (dateValue == null || StringUtils.isBlank(dateValue)) { + throw new IllegalArgumentException("Invalid method argument(s) to parseDateStrictly(" + dateValue + ", " + formatPattern + ")"); + } + + if (formatPattern == null || StringUtils.isBlank(formatPattern)) { + formatPattern = "uuuu-MM-dd"; + } + + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(formatPattern); + + parsedDate = LocalDate.parse(dateValue, dtf.withResolverStyle(ResolverStyle.STRICT)); + + return parsedDate; + } + + /** + * @param dateValue the date value in {@link String} + * @param formatPattern the date-time format pattern + * @return the parsed {@link LocalDateTime} + */ + public static LocalDateTime strictParseDateTime(String dateValue, String formatPattern) { + LocalDateTime parsedDateTime = null; + if (dateValue == null || StringUtils.isBlank(dateValue)) { + throw new IllegalArgumentException("Invalid method argument(s) to parseDateTimeStrictly(" + dateValue + ", " + formatPattern + ")"); + } + + if (formatPattern == null || StringUtils.isBlank(formatPattern)) { + formatPattern = "uuuu-MM-dd'T'HH:mm:ss.SSSS"; + } + + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(formatPattern); + + parsedDateTime = LocalDateTime.parse(dateValue, dtf.withResolverStyle(ResolverStyle.STRICT)); + + return parsedDateTime; + } + + public static String monthNumberToShortName(int monthNumber) { + return Month.of(monthNumber).getDisplayName(TextStyle.SHORT, Locale.getDefault()); + } + + public static String monthNumberToFullName(int monthNumber) { + return Month.of(monthNumber).getDisplayName(TextStyle.FULL, Locale.getDefault()); + } + + public static String monthNumberToName(int monthNumber) { + return Month.of(monthNumber).name(); + } + + public static int monthNameToNumber(String monthName) { + return Month.valueOf(monthName.toUpperCase()).getValue(); + } + + public static int monthShortNameToNumber(String abbreviation) { + Optional monthOptional = Arrays.stream(Month.values()) + .filter(month -> month.name().substring(0, 3).equalsIgnoreCase(abbreviation)) + .findFirst(); + + return monthOptional.orElseThrow(IllegalArgumentException::new).getValue(); + } + + public static String monthShortNameToFullName(String abbreviation) { + Optional monthOptional = Arrays.stream(Month.values()) + .filter(month -> month.name().substring(0, 3).equalsIgnoreCase(abbreviation)) + .findFirst(); + + return monthOptional.orElseThrow(IllegalArgumentException::new) + .getDisplayName(TextStyle.FULL, Locale.getDefault()); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/EOLUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/EOLUtils.java new file mode 100644 index 0000000..1a58461 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/EOLUtils.java @@ -0,0 +1,274 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public final class EOLUtils { + + /** + * Unix-style end-of-line marker (LF) + */ + private static final String EOL_UNIX = "\n"; + + /** + * Windows-style end-of-line marker (CRLF) + */ + private static final String EOL_WINDOWS = "\r\n"; + + /** + * "Old Mac"-style end-of-line marker (CR) + */ + private static final String EOL_OLD_MAC = "\r"; + + /** + * Default end-of-line marker on current system + */ + private static final String EOL_SYSTEM_DEFAULT = System.lineSeparator(); + + /** + * The support end-of-line marker modes + */ + public enum EOLMode { + /** + * Unix-style end-of-line marker ("\n") + */ + LF, + + /** + * Windows-style end-of-line marker ("\r\n") + */ + CRLF, + + /** + * "Old Mac"-style end-of-line marker ("\r") + */ + CR + } + + /** + * The default end-of-line marker mode for the current system + */ + public static final EOLMode SYSTEM_DEFAULT = (EOL_SYSTEM_DEFAULT.equals(EOL_UNIX) ? EOLMode.LF : (EOL_SYSTEM_DEFAULT + .equals(EOL_WINDOWS) ? EOLMode.CRLF : (EOL_SYSTEM_DEFAULT.equals(EOL_OLD_MAC) ? EOLMode.CR : null))); + + static { + // Just in case... + if (SYSTEM_DEFAULT == null) { + throw new IllegalStateException("Could not determine system default end-of-line marker"); + } + } + + /** + * Determines the end-of-line {@link EOLMode} of a text file. + * + * @param textFile the file to investigate + * @return the end-of-line {@link EOLMode} of the given file, or {@code null} if it could not be determined + * @throws Exception + */ + public static EOLMode determineEOL(File textFile) + throws Exception { + if (!textFile.exists()) { + throw new IOException("Could not find file to open: " + textFile.getAbsolutePath()); + } + + FileInputStream fileIn = new FileInputStream(textFile); + BufferedInputStream bufferIn = new BufferedInputStream(fileIn); + try { + int prev = -1; + int ch; + while ((ch = bufferIn.read()) != -1) { + if (ch == '\n') { + if (prev == '\r') { + return EOLMode.CRLF; + } else { + return EOLMode.LF; + } + } else if (prev == '\r') { + return EOLMode.CR; + } + prev = ch; + } + throw new Exception("Could not determine end-of-line marker mode"); + } catch (IOException ioe) { + throw new Exception("Could not determine end-of-line marker mode", ioe); + } finally { + // Clean up: + IOUtils.closeQuietly(bufferIn); + } + } + + /** + * Checks whether the given text file has Windows-style (CRLF) line endings. + * + * @param textFile the file to investigate + * @return + * @throws Exception + */ + public static boolean hasWindowsEOL(File textFile) + throws Exception { + return EOLMode.CRLF.equals(determineEOL(textFile)); + } + + /** + * Checks whether the given text file has Unix-style (LF) line endings. + * + * @param textFile the file to investigate + * @return + * @throws Exception + */ + public static boolean hasUnixEOL(File textFile) + throws Exception { + return EOLMode.LF.equals(determineEOL(textFile)); + } + + /** + * Checks whether the given text file has "Old Mac"-style (CR) line endings. + * + * @param textFile the file to investigate + * @return + * @throws Exception + */ + public static boolean hasOldMacEOL(File textFile) + throws Exception { + return EOLMode.CR.equals(determineEOL(textFile)); + } + + /** + * Checks whether the given text file has line endings that conform to the system default mode (e.g. LF on Unix). + * + * @param textFile the file to investigate + * @return + * @throws Exception + */ + public static boolean hasSystemDefaultEOL(File textFile) + throws Exception { + return SYSTEM_DEFAULT.equals(determineEOL(textFile)); + } + + /** + * Convert the line endings in the given file to Unix-style (LF). + * + * @param textFile the file to process + * @throws IOException + */ + public static void convertToUnixEOL(File textFile) + throws IOException { + convertLineEndings(textFile, EOL_UNIX); + } + + /** + * Convert the line endings in the given file to Windows-style (CRLF). + * + * @param textFile the file to process + * @throws IOException + */ + public static void convertToWindowsEOL(File textFile) + throws IOException { + convertLineEndings(textFile, EOL_WINDOWS); + } + + /** + * Convert the line endings in the given file to "Old Mac"-style (CR). + * + * @param textFile the file to process + * @throws IOException + */ + public static void convertToOldMacEOL(File textFile) + throws IOException { + convertLineEndings(textFile, EOL_OLD_MAC); + } + + /** + * Convert the line endings in the given file to the system default mode. + * + * @param textFile the file to process + * @throws IOException + */ + public static void convertToSystemEOL(File textFile) + throws IOException { + convertLineEndings(textFile, EOL_SYSTEM_DEFAULT); + } + + /** + * Line endings conversion method. + * + * @param textFile the file to process + * @param eol the end-of-line marker to use (as a {@link String}) + * @throws IOException + */ + private static void convertLineEndings(File textFile, String eol) + throws IOException { + File temp = null; + BufferedReader bufferIn = null; + BufferedWriter bufferOut = null; + + try { + if (textFile.exists()) { + // Create a new temp file to write to + temp = new File(textFile.getAbsolutePath() + ".normalized"); + + boolean isCreated = temp.createNewFile(); + + if (!isCreated) { + throw new IOException("Could not create normailzed file: " + temp.getAbsolutePath()); + } + + // Get a stream to read from the file un-normalized file + FileInputStream fileIn = new FileInputStream(textFile); + DataInputStream dataIn = new DataInputStream(fileIn); + bufferIn = new BufferedReader(new InputStreamReader(dataIn)); + + // Get a stream to write to the normalized file + FileOutputStream fileOut = new FileOutputStream(temp); + DataOutputStream dataOut = new DataOutputStream(fileOut); + bufferOut = new BufferedWriter(new OutputStreamWriter(dataOut)); + + // For each line in the un-normalized file + String line; + while ((line = bufferIn.readLine()) != null) { + // Write the original line plus the operating-system dependent newline + bufferOut.write(line); + bufferOut.write(eol); // write EOL marker + } + + // Close buffered reader & writer: + bufferIn.close(); + bufferOut.close(); + + // Remove the original file + boolean isDeleted = textFile.delete(); + + if (!isDeleted) { + throw new IOException("Could not delete file: " + textFile.getAbsolutePath()); + } + + // And rename the original file to the new one + boolean isRenamed = temp.renameTo(textFile); + + if (!isRenamed) { + throw new IOException("Could not rename file: " + temp.getAbsolutePath()); + } + } else { + // If the file doesn't exist... + throw new IOException("Could not find file to open: " + textFile.getAbsolutePath()); + } + } finally { + // Clean up, temp should never exist + FileUtils.deleteQuietly(temp); + IOUtils.closeQuietly(bufferIn); + IOUtils.closeQuietly(bufferOut); + } + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/FileUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/FileUtils.java new file mode 100644 index 0000000..798105e --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/FileUtils.java @@ -0,0 +1,142 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public final class FileUtils { + + private FileUtils() { + } + + /** + * @param sourceFile the source file + * @param targetFile the target file + */ + public static void gzipCompress(String sourceFile, String targetFile) { + try (FileOutputStream fos = new FileOutputStream(targetFile); + GZIPOutputStream gzos = new GZIPOutputStream(fos); + FileInputStream fis = new FileInputStream(sourceFile)) { + + byte[] buffer = new byte[1024]; + int length; + + while ((length = fis.read(buffer)) > 0) { + gzos.write(buffer, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * @param sourceFile + * @param targetFile + */ + public static void gzipDecompress(String sourceFile, String targetFile) { + try (FileInputStream fis = new FileInputStream(sourceFile); + GZIPInputStream gzis = new GZIPInputStream(fis); + FileOutputStream fos = new FileOutputStream(targetFile)) { + + byte[] buffer = new byte[1024]; + int length; + while ((length = gzis.read(buffer)) > 0) { + fos.write(buffer, 0, length); + } + } catch (IOException e) { + System.err.println(e.getCause() + ": " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * @param source + * @param destination + */ + public static void copyFiles(File source, File destination) { + try (InputStream is = Files.newInputStream(source.toPath()); OutputStream os = Files.newOutputStream(destination.toPath())) { + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } catch (IOException ioe) { + System.err.println(ioe.getCause() + ": " + ioe.getMessage()); + throw new RuntimeException(ioe); + } + } + + /** + * @param source the source file name or path + * @param destination the destination file name or path + * @throws IOException + */ + public static void copyFile(String source, String destination) throws IOException { + try (InputStream is = Files.newInputStream(new File(source).toPath()); + OutputStream os = Files.newOutputStream(new File(destination).toPath())) { + int length; + byte[] bytes = new byte[1024]; + // copy data from input stream to output stream + while ((length = is.read(bytes)) != -1) { + os.write(bytes, 0, length); + } + } catch (IOException ioe) { + throw ioe; + } + } + + /** + * @param source the source file + * @param destination the destination file + * @throws IOException + */ + public static void copyFile(File source, File destination) throws IOException { + try (FileInputStream fis = new FileInputStream(source); + FileOutputStream fos = new FileOutputStream(destination); + FileChannel sourceChannel = fis.getChannel(); + FileChannel destChannel = fos.getChannel()) { + destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()); + } + } + + /** + * @param directory the directory to delete + */ + public static boolean removeDirectory(File directory) { + boolean result = false; + if (directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File aFile : files) { + removeDirectory(aFile); + } + } + result = directory.delete(); + } else { + result = directory.delete(); + } + + return result; + } + + /** + * @param directory the directory to clean + */ + public static void cleanDirectory(File directory) { + if (directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File aFile : files) { + removeDirectory(aFile); + } + } + } + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/HibernateUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/HibernateUtils.java new file mode 100644 index 0000000..822ef3c --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/HibernateUtils.java @@ -0,0 +1,161 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.CriteriaUpdate; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.hibernate.Session; +import org.hibernate.Query; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public final class HibernateUtils { + + private HibernateUtils() { + } + + private static Query queryHelperBasedOnColumn(Session session, Class clazz, Map map) { + StringBuilder queryString = new StringBuilder("FROM ").append(clazz.getSimpleName()).append(" WHERE "); + map.forEach((key, value) -> queryString.append(key).append(" = :").append(key).append(" AND ")); + + // Remove the trailing " AND " from the query string + if (!map.isEmpty()) { + queryString.delete(queryString.length() - 5, queryString.length()); + } + + Query query = session.createQuery(queryString.toString(), clazz); + map.forEach(query::setParameter); + + return query; + } + + /** + * Delete query helper + * + * @param session the hibernate session + * @param clazz the class + * @param map the parameter map + * @param the generic class + * @return + */ + public static int deleteEqualToColumn(Session session, Class clazz, Map map) { + if (session == null) { + throw new NullPointerException("Hibernate session can not be null"); + } + CriteriaBuilder cb = (CriteriaBuilder) session.getCriteriaBuilder(); + CriteriaDelete delete = cb.createCriteriaDelete(clazz); + Root root = delete.from(clazz); + + delete.where(getPredicates(map, cb, root)); + + return session.createQuery(delete).executeUpdate(); + } + + /** + * Update query helper + * + * @param session + * @param clazz + * @param whereMap + * @param updateMap + * @param + * @return + */ + public static int updateHelper(Session session, Class clazz, Map whereMap, Map updateMap) { + if (session == null) { + throw new NullPointerException("Hibernate session can not be null"); + } + CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + CriteriaUpdate update = criteriaBuilder.createCriteriaUpdate(clazz); + Root root = update.from(clazz); + + updateMap.forEach((column, value) -> { + update.set(column, value); + }); + + update.where(getPredicates(whereMap, criteriaBuilder, root)); + return session.createQuery(update).executeUpdate(); + } + + /** + * Select query helper based on columns + * + * @param session + * @param clazz + * @param map + * @param + * @return + */ + public static List selectHelperBasedOnCol(Session session, Class clazz, Map map) { + return queryHelperBasedOnCol(session, clazz, map).getResultList(); + } + + /** + * Select query helper + * + * @param session + * @param clazz + * @param + * @return + */ + public static List selectHelper(Session session, Class clazz) { + return queryHelperBasedOnCol(session, clazz, Collections.EMPTY_MAP).getResultList(); + } + + public static Stream selectStreamBasedOnCol(Session session, Class clazz, Map map) { + Query query = queryHelperBasedOnCol(session, clazz, map); + return query != null ? query.getResultStream() : Stream.empty(); + } + + public static Long selectCountHelperBasedOnCol(Session session, Class clazz, Map map) { + if (session == null) { + throw new NullPointerException("Hibernate session can not be null"); + } + + CriteriaBuilder builder = (CriteriaBuilder) session.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = + builder.createQuery(Long.class); + Root root = criteriaQuery.from(clazz); + + if (!map.isEmpty()) { + criteriaQuery.where(getPredicates(map, builder, root)); + } + + criteriaQuery.select(builder.count(root)); + + return session.createQuery(criteriaQuery).getSingleResult(); + } + + private static Predicate[] getPredicates(Map map, CriteriaBuilder criteriaBuilder, Root root) { + int size = map.keySet().size(); + Predicate[] predicates = new Predicate[size]; + List list = new ArrayList<>(); + map.forEach((column, value) -> { + list.add(criteriaBuilder.equal(root.get(column), value)); + }); + return list.toArray(predicates); + } + + private static Query queryHelperBasedOnCol(Session session, Class clazz, Map map) { + if (session == null) { + throw new NullPointerException("Hibernate session can not be null"); + } + + CriteriaBuilder builder = (CriteriaBuilder) session.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = builder.createQuery(clazz); + Root root = criteriaQuery.from(clazz); + + if (!map.isEmpty()) { + criteriaQuery.where(getPredicates(map, builder, root)); + } + + return session.createQuery(criteriaQuery); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/MathUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/MathUtils.java new file mode 100644 index 0000000..1cefdc9 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/MathUtils.java @@ -0,0 +1,54 @@ +package com.shortthirdman.primekit.essentials.common.util; + +public final class MathUtils { + + private MathUtils() { + } + + /** + * Calculate the variance for collection of elements + * + * @param array the input array + * @param n the number of array elemnts + * @return + */ + public static double variance(double[] array, int n) { + double sum = 0; + for (int i = 0; i < n; i++) + sum += array[i]; + double mean = sum / (double) n; + + double sqDiff = 0; + for (int i = 0; i < n; i++) + sqDiff += (array[i] - mean) * (array[i] - mean); + + return sqDiff / n; + } + + /** + * Calculate the standard deviation for collection of elements + * + * @param array the input Double array + * @param n the number of array elemnts + * @return + */ + public static double standardDeviation(double[] array, int n) { + return Math.sqrt(variance(array, n)); + } + + /** + * Calculate the average for collection of elements + * + * @param array the input array + * @param n the number of elements in array + * @return + */ + public static double average(double[] array, int n) { + double sum = 0; + double finalsum = 0; + for (double i : array) { + finalsum = (sum += i); + } + return finalsum / n; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NetworkUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NetworkUtils.java new file mode 100644 index 0000000..ca2cb0b --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NetworkUtils.java @@ -0,0 +1,215 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.net.*; +import java.util.*; + +public final class NetworkUtils { + + private NetworkUtils() { + } + + /** + * @return List + * @throws SocketException + */ + public static List getAllAvailableInterfaces() throws SocketException { + List networkInterfaces = new ArrayList<>(); + NetworkInterface intf; + + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { + intf = en.nextElement(); + networkInterfaces.add(intf); + } + + return networkInterfaces; + } + + /** + * @return List + * @throws SocketException + */ + public static List getAllInterfaces() throws SocketException { + List allInterfaces = new ArrayList<>(); + + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) { + NetworkInterface networkInterface = interfaces.nextElement(); + allInterfaces.add(networkInterface); + + Enumeration subInterfaces = networkInterface.getSubInterfaces(); + + if (subInterfaces.hasMoreElements()) { + while (subInterfaces.hasMoreElements()) { + allInterfaces.add(subInterfaces.nextElement()); + } + } + } + + allInterfaces.sort(Comparator.comparingInt(NetworkInterface::getIndex)); + + return allInterfaces; + } + + /** + * Get all the interfaces which are not loopback ones + * + * @return Set + */ + public static Set getAllLocalInterfaces() { + Set netInfSet = new HashSet<>(); + try { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + + while (e.hasMoreElements()) { + NetworkInterface ni = e.nextElement(); + + Enumeration set = ni.getInetAddresses(); + + if (set.hasMoreElements()) { + InetAddress address = set.nextElement(); + if (!address.isLoopbackAddress()) { + netInfSet.add(ni); + } + } + } + } catch (SocketException se) { + se.printStackTrace(); + } + + return netInfSet; + } + + /** + * @return NetworkInterface + */ + public static NetworkInterface getAvailableNetworkInterface() { + NetworkInterface result = null; + + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + if (networkInterface.isLoopback()) + continue; + + if (networkInterface.isUp()) { + result = networkInterface; + break; + } + } + } catch (SocketException se) { + se.printStackTrace(); + } + + return result; + } + + /** + * Get broadcast address + * @return String + * @throws SocketException + */ + public static String getBroadcast() throws SocketException { + String result = null; + System.setProperty("java.net.preferIPv4Stack", "true"); + for (Enumeration niEnum = NetworkInterface.getNetworkInterfaces(); niEnum + .hasMoreElements(); ) { + NetworkInterface ni = niEnum.nextElement(); + if (!ni.isLoopback()) { + for (InterfaceAddress interfaceAddress : ni.getInterfaceAddresses()) { + InetAddress broadcast = interfaceAddress.getBroadcast(); + if (broadcast != null) { + result = broadcast.toString().substring(1); + break; + } + } + if (result != null) break; + } + } + return result; + } + + /** + * @param subIf the network sub-interface + * @param subInterface true, if querying for sub-interface + * @return String + * @throws SocketException + */ + public static String describeInterface(NetworkInterface subIf, boolean subInterface) throws SocketException { + StringBuilder description = new StringBuilder(); + String padding = ""; + + if (subInterface) { + description.append("\t\tSUBINTERFACE"); + description.append("\t\t____________"); + padding = "\t\t"; + } + + description.append(String.format("%sInterface Display name: %s\n", padding, subIf.getDisplayName())); + description.append(String.format("%sInterface Name: %s\n", padding, subIf.getName())); + Enumeration inetAddresses = subIf.getInetAddresses(); + + for (InetAddress inetAddress : Collections.list(inetAddresses)) { + description.append(String.format("%s\tInetAddress: %s\n", padding, inetAddress)); + } + + description.append(String.format("%s\t\tUp? %s\n", padding, subIf.isUp())); + description.append(String.format("%s\t\tLoopback? %s\n", padding, subIf.isLoopback())); + description.append(String.format("%s\t\tPointToPoint? %s\n", padding, subIf.isPointToPoint())); + description.append(String.format("%s\t\tSupports multicast? %s\n", padding, subIf.supportsMulticast())); + description.append(String.format("%s\t\tVirtual? %s\n", padding, subIf.isVirtual())); + description.append(String.format("%s\t\tHardware address: %s\n", padding, + Arrays.toString(subIf.getHardwareAddress()))); + description.append(String.format("%s\t\tMTU: %s\n", padding, subIf.getMTU())); + description.append("\n"); + + return description.toString(); + } + + /** + * Verify if CIDR string is a valid CIDR address. + * + * @param network CIDR to verify + * @throws IllegalArgumentException + */ + public static void isCIDR(String network) throws IllegalArgumentException { + String[] hostMask = network.split("/"); + if (hostMask.length != 2) { + throw new IllegalArgumentException("subnetAddress is not a CIDR"); + } + + isValidInetAddress(hostMask[0]); + + // Mask must be < 32 + if (Integer.parseUnsignedInt(hostMask[1]) > 32) { + throw new IllegalArgumentException("CIDR mask may not be larger than 32"); + } + } + + /** + * Verify if IP string is an IPv4 address. + * + * @param ipAddress IP to verify + * @throws IllegalArgumentException + */ + public static void isValidInetAddress(String ipAddress) throws IllegalArgumentException { + if (ipAddress == null || ipAddress.isEmpty()) { + throw new IllegalArgumentException("IP address is missing or empty"); + } + + if (ipAddress.contains(":")) { + // implement IPv6 validation + } else { + String[] segments = ipAddress.split("\\."); + if (segments.length != 4) { + throw new IllegalArgumentException("IP does not appear valid:" + ipAddress); + } + // it appears to be literal IP, its safe to use the getByName method + try { + InetAddress address = InetAddress.getByName(ipAddress); + } catch (UnknownHostException uhe) { + throw new RuntimeException(uhe); + } + } + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NumberUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NumberUtils.java new file mode 100644 index 0000000..0134a21 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/NumberUtils.java @@ -0,0 +1,154 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import com.shortthirdman.primekit.essentials.common.GenericConstants; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; + +public final class NumberUtils { + + private NumberUtils() { + } + + /** + * @param amount + * @param precision + * @param pattern + * @param locale + * @return + */ + public static String formatCurrency(double amount, int precision, String pattern, Locale locale) { + NumberFormat nf = NumberFormat.getCurrencyInstance(locale); + DecimalFormat df = (DecimalFormat) nf; + df.setMinimumFractionDigits(precision); + df.setMaximumFractionDigits(precision); + df.setDecimalSeparatorAlwaysShown(true); + df.applyPattern(pattern); + return df.format(amount); + } + + /** + * @param amount + * @param precision + * @param pattern + * @param locale + * @return + */ + public static String formatNumber(double amount, int precision, String pattern, Locale locale) { + NumberFormat nf = NumberFormat.getNumberInstance(locale); + DecimalFormat df = (DecimalFormat) nf; + df.setMinimumFractionDigits(precision); + df.setMaximumFractionDigits(precision); + df.setDecimalSeparatorAlwaysShown(true); + df.applyPattern(pattern); + return df.format(amount); + } + + /** + * @param amount + * @param precision + * @param locale + * @return + */ + public static String formatCurrency(double amount, int precision, Locale locale) { + NumberFormat nf = NumberFormat.getCurrencyInstance(locale); + nf.setMinimumFractionDigits(precision); + nf.setMaximumFractionDigits(precision); + return nf.format(amount); + } + + /** + * @param amount + * @param precision + * @param locale + * @return + */ + public static String formatNumber(double amount, int precision, Locale locale) { + NumberFormat nf = NumberFormat.getNumberInstance(locale); + nf.setMinimumFractionDigits(precision); + nf.setMaximumFractionDigits(precision); + return nf.format(amount); + } + + /** + * @param number the input number object + * @param pattern the pattern format + * @return the String value of converted number + */ + public static String changeToDecimalFormat(Object number, String pattern) { + BigDecimal bdNumber = new BigDecimal(number.toString()); + bdNumber = bdNumber.stripTrailingZeros(); //Returns a BigDecimal with any trailing zero's removed + pattern = (pattern == null) ? "###,##0.0###########" : pattern; //To apply formatting when the number of digits in input equals the pattern + DecimalFormat newFormat = new DecimalFormat(pattern, new DecimalFormatSymbols(Locale.US)); + return newFormat.format(bdNumber); + } + + /** + * @param number the input number object + * @return the double value of converted number + */ + public static double removeCommasFromNumber(Object number) { + try { + StringBuilder inputNo = new StringBuilder(number.toString()); + if (!inputNo.isEmpty()) { + while (inputNo.indexOf(GenericConstants.COMMA.getValue()) != -1) { + inputNo.deleteCharAt(inputNo.indexOf(GenericConstants.COMMA.getValue())); + } + } else { + return 0.0; + } + return Double.parseDouble(inputNo.toString()); + } catch (NumberFormatException e) { + return 0.0; + } + } + + /** + * @param bigDecimalValue the input big decimal value in string + * @param precision the precision value + * @return the string formatted value + */ + public static String changeToRequiredDecimals(String bigDecimalValue, int precision) { + if (bigDecimalValue == null || bigDecimalValue.isEmpty()) { + return "0.0"; + } + + StringBuilder newFormattedString = new StringBuilder(); + String afterDecimal = null; + if (bigDecimalValue.contains(GenericConstants.DOT_PERIOD.getValue())) { + afterDecimal = bigDecimalValue.substring(bigDecimalValue + .indexOf(GenericConstants.DOT_PERIOD.getValue()) + 1); + int length = Math.abs((afterDecimal.length() - precision)); + if (afterDecimal.length() < precision) { + newFormattedString = new StringBuilder(bigDecimalValue); + for (int i = 0; i < length; i++) { + newFormattedString.append(GenericConstants.ZERO.getValue()); + } + } else if (afterDecimal.length() > precision) { + newFormattedString = new StringBuilder(bigDecimalValue.substring(0, + bigDecimalValue.length() - length)); + if (precision == 0) { + newFormattedString = new StringBuilder(newFormattedString.substring(0, + newFormattedString.toString().indexOf(GenericConstants.DOT_PERIOD.getValue()))); + } else { + newFormattedString = new StringBuilder(bigDecimalValue); + } + + } else { + if (precision > 0) { + newFormattedString = new StringBuilder(bigDecimalValue + GenericConstants.DOT_PERIOD.getValue()); + } else { + newFormattedString = new StringBuilder(bigDecimalValue); + } + for (int i = 0; i < precision; i++) { + newFormattedString = new StringBuilder(newFormattedString + GenericConstants.ZERO.getValue()); + } + } + } + + return newFormattedString.toString(); + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/PostgreSQLUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/PostgreSQLUtils.java new file mode 100644 index 0000000..bfde2e7 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/PostgreSQLUtils.java @@ -0,0 +1,43 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import org.apache.commons.lang3.StringUtils; +import org.postgresql.ds.PGSimpleDataSource; +import org.postgresql.jdbc.PgConnection; + +import java.sql.SQLException; + +public class PostgreSQLUtils { + + public static void createNewSchema(String schemaName) { + if (schemaName == null || StringUtils.isBlank(schemaName)) { + throw new IllegalArgumentException("Schema name is null. Unable to proceed for schema creation"); + } + + PGSimpleDataSource dataSource = new PGSimpleDataSource(); + dataSource.setURL(System.getenv("PG_HOST")); + dataSource.setUser(System.getenv("PG_USER")); + dataSource.setPassword(System.getenv("PG_PASSWORD")); + + try (PgConnection connection = (PgConnection) dataSource.getConnection()) { + // Get a connection + var schemas = connection.getMetaData().getSchemas(); + + // Check if the schema exists + boolean schemaExists = false; + + if (schemas.next()) { + // schemas + // contains(schemaName) + schemaExists = true; + } + + if (schemaExists) { + System.out.println("Schema exists."); + } else { + System.out.println("Schema does not exist."); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/StringUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/StringUtils.java new file mode 100644 index 0000000..1dacba3 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/StringUtils.java @@ -0,0 +1,290 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import com.shortthirdman.primekit.essentials.common.GenericConstants; + +import java.text.Normalizer; +import java.util.*; +import java.util.stream.Collectors; + +public final class StringUtils { + + private StringUtils() { + } + + /** + * Convert a snake-case text string to camel-case + * + * @apiNote Capitalize first letter of string. Replace the first occurrence of + * letter that present after the underscore, to capitalize form of next + * letter of underscore + * @param text the input string to convert + * @return the camel-case converted text string + */ + public static String snakeToCamel(String text) { + text = text.substring(0, 1).toUpperCase() + text.substring(1); + while (text.contains(GenericConstants.UNDERSCORE.getValue())) { + text = text.replaceFirst("_[a-z]", + String.valueOf(Character.toUpperCase(text.charAt(text.indexOf(GenericConstants.UNDERSCORE.getValue()) + 1)))); + } + return text; + } + + /** + * Convert a camel-case text string to snake-case + * + * @apiNote Replace the given regex with replacement string and convert it to + * lower case. + * @param text the input string to convert + * @return snake-case converted text string + */ + public static String camelToSnake(String text) { + String regex = "([a-z])([A-Z]+)"; + String replacement = "$1_$2"; + text = text.replaceAll(regex, replacement).toLowerCase(); + return text; + } + + /** + * Convert a camel-case text string to snake-case + * + * @param text the input string to convert + * @param upper true if result snake cased string should be upper cased + * @return + */ + public static String camelToSnake(final String text, final boolean upper) { + String ret = text.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2").replaceAll("([a-z])([A-Z])", "$1_$2"); + + if (upper) { + return ret.toUpperCase(); + } else { + return ret.toLowerCase(); + } + } + + /** + * Converts a delimited text string into list of string + * + * @param delimitedText + * @param delimiter + * @return + */ + public static List convertToList(String delimitedText, String delimiter) { + List result = List.of(); + + try { + String[] commaSeparatedArr = delimitedText.split(delimiter); + result = Arrays.stream(commaSeparatedArr).collect(Collectors.toList()); + } catch (Exception e) { + throw new RuntimeException("Unexpected error caught while converting input text to list: " + e.getMessage()); + } + + return result; + } + + /** + * @param textValue the source input text to trim + * @return the trimmed value + */ + public static String trimText(String textValue) { + String trimmedText; + if (textValue == null) { + trimmedText = null; + } else { + trimmedText = textValue.trim(); + } + return trimmedText; + } + + /** + * Given two strings source and target, determine if they are isomorphic.
+ * Two strings are isomorphic if the characters in source can be replaced to get target. + * + * @param s the source text + * @param t the target text + * @return true if two given strings are isomorphic + */ + public boolean isIsomorphic(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + Map map1 = new HashMap<>(); + Map map2 = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char c1 = s.charAt(i); + char c2 = t.charAt(i); + + if (map1.containsKey(c1)) { + if (c2 != map1.get(c1)) { + return false; + } + } else { + if (map2.containsKey(c2)) { + return false; + } + + map1.put(c1, c2); + map2.put(c2, c1); + } + } + + return true; + } + + /** + * Helper method to convert a byte[] array (such as a MsgId) to a hex string + * + * @param array + * @return hex string + */ + public static String arrayToHexString(byte[] array) { + if (array == null || array.length == 0) { + return null; + } + + StringBuilder string = new StringBuilder(); + + for (byte b : array) { + String hexString = Integer.toHexString(0x00FF & b); + string.append(hexString.length() == 1 ? "0" + hexString : hexString); + } + return string.toString(); + } + + /** + * Convert a byte[] array (such as a MsgId) to a hex string + * + * @param array + * @param offset + * @param limit + * @return hex string + */ + public static String arrayToHexString(byte[] array, int offset, int limit) { + String retVal = null; + + if (array == null || array.length == 0) { + return retVal; + } + + StringBuilder hexString = new StringBuilder(array.length); + int hexVal; + char hexChar; + int length = Math.min(limit, array.length); + for (int i = offset; i < length; i++) { + hexVal = (array[i] & 0xF0) >> 4; + hexChar = (char) ((hexVal > 9) ? ('A' + (hexVal - 10)) : ('0' + hexVal)); + hexString.append(hexChar); + hexVal = array[i] & 0x0F; + hexChar = (char) ((hexVal > 9) ? ('A' + (hexVal - 10)) : ('0' + hexVal)); + hexString.append(hexChar); + } + retVal = hexString.toString(); + + return retVal; + } + + /** + * Checks each char in a string to see if the string contains + * any char values greater than those used in ASCII. + * + * @param source Input string to search for Non-ASCII chars. + * @return true, if any char is greater than u007f, false otherwise + */ + public static boolean containsNonAscii(String source) { + if (source == null) { + throw new IllegalArgumentException(""); + } + + source = Normalizer.normalize(source, Normalizer.Form.NFD); + + for (char c : source.toCharArray()) { + if (c >= '\u007F') { + return true; + } + } + + return false; + } + + public static byte[] asciiToHex(byte[] src, int len, int padding) { + byte[] asc; + + if (src == null || len == 0) { + return new byte[0]; + } + + byte[] bcd = new byte[len]; + + if (padding == 0) { + asc = getLeftPartitionBytes(src, len * 2, (byte) 0x30); + } else { + asc = getRightPartitionBytes(src, len * 2, (byte) 0x30); + } + + for (int i = 0; i < len; i++) { + bcd[i] = (byte) (convertByteToBCD(asc[i * 2]) << 4 ^ (convertByteToBCD(asc[i * 2 + 1]) & 0x0f)); + } + + return bcd; + } + + private static byte[] getLeftPartitionBytes(final byte[] src, final int len, final byte fill) { + byte[] des = new byte[len]; + + int lLen = Math.min(src.length, len); + int rLen = Math.max(src.length, len); + + for (int i = 0; i < len; i++) { + if (i >= (rLen - lLen)) + des[i] = src[i - rLen + lLen]; + else + des[i] = fill; + } + + return des; + } + + /** + * @param src the source byte array + * @param len the length of source bytes + * @param fill the fill byte character + * @return the right partitioned byte array from source + */ + private static byte[] getRightPartitionBytes(final byte[] src, final int len, final byte fill) { + byte[] result = new byte[len]; + + int lLen = Math.min(src.length, len); + int rLen = Math.max(src.length, len); + + for (int i = 0; i < lLen; i++) { + if (i < lLen) { + result[i] = src[i]; + } else { + result[i] = fill; + } + } + + return result; + } + + /** + * Convert byte to BCD + + * @param src the source bytes + * @return the converted BCD byte + */ + private static byte convertByteToBCD(byte src) { + byte re = src; + + if (src <= 0x39 && src >= 0x30) { + re = (byte) (src - 0x30); + } else if (src <= 0x46 && src >= 0x41) { + re = (byte) (src - 0x37); + } else if (src <= 0x66 && src >= 0x61) { + re = (byte) (src - 0x57); + } + + return re; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/SystemUtils.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/SystemUtils.java new file mode 100644 index 0000000..c8014cc --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/common/util/SystemUtils.java @@ -0,0 +1,169 @@ +package com.shortthirdman.primekit.essentials.common.util; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; + +public final class SystemUtils { + + private SystemUtils() { + } + + /** + * Returns the free Java Virtual Machine memory in bytes + * @return long + */ + public static long getFreeMemory() { + Runtime runtime = Runtime.getRuntime(); + return runtime.freeMemory(); + } + + /** + * Returns the maximum Java Virtual Machine memory in bytes + * @return long + */ + public static long getMaxMemory() { + Runtime runtime = Runtime.getRuntime(); + return runtime.maxMemory(); + } + + /** + * Returns the total Java Virtual Machine memory in bytes + * @return long + */ + public static long getTotalMemory() { + Runtime runtime = Runtime.getRuntime(); + return runtime.totalMemory(); + } + + /** + * Kill system process + * @param port the port number of process to kill + */ + public static void killProcess(int port) { + + int pid = getPid(port); + + if (pid == 0) { + return; + } + + String[] command = { "taskkill", "/F", "/T", "/PID", Integer.toString(pid) }; + if (System.getProperty("os.name").startsWith("Linux")) { + command = new String[]{ "kill", "-9", Integer.toString(pid) }; + } + + try { + Process killer = Runtime.getRuntime().exec(command); + int result = killer.waitFor(); + System.out.println("Killed pid " + pid + " exitValue: " + result); + + } catch (IOException | InterruptedException e) { + throw new RuntimeException("Unknown error caught while killing process: " + e.getMessage()); + } + } + + public static int getPid(int port) { + if (System.getProperty("os.name").startsWith("Windows")) { + return getPidWin(port); + } else { + return getPidLinux(port); + } + } + + /** + * @param port the port number of Windows process + * @return int + */ + private static int getPidWin(int port) { + String[] command = { "netstat", "-on" }; + try { + Process netstat = Runtime.getRuntime().exec(command); + + StringBuilder connectionList = new StringBuilder(); + Reader reader = new InputStreamReader(netstat.getInputStream()); + char[] buffer = new char[1024]; + + for (int n = reader.read(buffer); n != -1; n = reader.read(buffer)) { + connectionList.append(buffer, 0, n); + } + + reader.close(); + String[] connections = connectionList.toString().split("\n"); + int portIdx = 10000; + String pid = null; + + for (String connection : connections) { + int idx = connection.indexOf(":" + port); + + if (idx == -1 || idx > portIdx) { + continue; + } + + String state = "ESTABLISHED"; + int stateIdx = connection.indexOf(state); + + if (stateIdx == -1) { + continue; + } + + portIdx = idx; + idx = stateIdx + state.length(); + pid = connection.substring(idx).trim(); + } + + if (pid != null) { + return Integer.parseInt(pid); + } + + } catch (Exception e) { + throw new RuntimeException("Unknown error caught while getting PID of Windows machine: " + e.getMessage()); + } + + return 0; + + } + + /** + * @param port the port number of Linux process + * @return int + */ + private static int getPidLinux(int port) { + String[] command = { "netstat", "-anp" }; + try { + Process netstat = Runtime.getRuntime().exec(command); + + StringBuilder connectionList = new StringBuilder(); + Reader reader = new InputStreamReader(netstat.getInputStream()); + char[] buffer = new char[1024]; + + for (int n = reader.read(buffer); n != -1; n = reader.read(buffer)) { + connectionList.append(buffer, 0, n); + } + reader.close(); + + String[] connections = connectionList.toString().split("\n"); + String pid = null; + + for (String connection : connections) { + if (connection.contains(":" + port) && connection.contains("/soffice.bin")) { + int idx = connection.indexOf("/soffice.bin"); + int idx2 = idx; + while (Character.isDigit(connection.charAt(--idx2))) { + System.out.println(""); + } + pid = connection.substring(idx2 + 1, idx); + } + } + + if (pid != null) { + return Integer.parseInt(pid); + } + + } catch (Exception e) { + throw new RuntimeException("Unknown error caught while getting PID of Linux machine: " + e.getMessage()); + } + + return 0; + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/core/annotations/.gitkeep b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/core/annotations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseController.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseController.java new file mode 100644 index 0000000..7de7902 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseController.java @@ -0,0 +1,9 @@ +package com.shortthirdman.primekit.essentials.generics.webmvc; + +import lombok.RequiredArgsConstructor; + +import java.io.Serializable; + +@RequiredArgsConstructor +public class AbstractBaseController { +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseDTO.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseDTO.java new file mode 100644 index 0000000..6ff2b00 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseDTO.java @@ -0,0 +1,18 @@ +package com.shortthirdman.primekit.essentials.generics.webmvc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.time.LocalDateTime; + +public class AbstractBaseDTO { + + private Long id; + + private int version; + + @JsonIgnoreProperties + private LocalDateTime createdAt; + + @JsonIgnoreProperties + private LocalDateTime updatedAt; +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseEntity.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseEntity.java new file mode 100644 index 0000000..492c624 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseEntity.java @@ -0,0 +1,35 @@ +package com.shortthirdman.primekit.essentials.generics.webmvc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.*; +import lombok.Data; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@EntityListeners(AuditingEntityListener.class) +public class AbstractBaseEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Version + private int version; + + @JsonIgnoreProperties + private LocalDateTime createdAt; + + @JsonIgnoreProperties + private LocalDateTime updatedAt; + + @PrePersist + public void onPrePersist() { + } + + @PreUpdate + public void onPreUpdate() { + } +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseRepository.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseRepository.java new file mode 100644 index 0000000..0c2f25c --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseRepository.java @@ -0,0 +1,11 @@ +package com.shortthirdman.primekit.essentials.generics.webmvc; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.NoRepositoryBean; + +import java.io.Serializable; + +@NoRepositoryBean +public interface AbstractBaseRepository extends JpaRepository { + +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseService.java b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseService.java new file mode 100644 index 0000000..3cb8285 --- /dev/null +++ b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/generics/webmvc/AbstractBaseService.java @@ -0,0 +1,21 @@ +package com.shortthirdman.primekit.essentials.generics.webmvc; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.Serializable; + +@Service +@Transactional +@RequiredArgsConstructor +public abstract class AbstractBaseService { + + @Autowired + AbstractBaseRepository repository; + + Class dtoClass; + + Class entityClass; +} diff --git a/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/webclient/.gitkeep b/primekit-essentials/src/main/java/com/shortthirdman/primekit/essentials/webclient/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/ArrayUtilsTest.java b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/ArrayUtilsTest.java new file mode 100644 index 0000000..9b6da88 --- /dev/null +++ b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/ArrayUtilsTest.java @@ -0,0 +1,4 @@ +package com.shortthirdman.primekit.essentials; + +public class ArrayUtilsTest { +} diff --git a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/CollectionUtilsTests.java b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/CollectionUtilsTests.java new file mode 100644 index 0000000..8f706eb --- /dev/null +++ b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/CollectionUtilsTests.java @@ -0,0 +1,56 @@ +package com.shortthirdman.primekit.essentials; + +import com.shortthirdman.primekit.essentials.common.util.CollectionUtils; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CollectionUtilsTests { + + @Test + public void distinctByKeysTest() { + // Test with distinct keys + Predicate predicate = CollectionUtils.distinctByKeys(String::length); + assertTrue(predicate.test("apple")); + assertTrue(predicate.test("banana")); + + // Test with duplicate keys + assertFalse(predicate.test("orange")); + assertTrue(predicate.test("grape")); + + // Test with null input + assertThrows(NullPointerException.class, () -> CollectionUtils.distinctByKeys((Function) null)); + } + + @Test + public void sortByValueTest() { + // Test with normal map + Map map = new HashMap<>(); + map.put("apple", 5); + map.put("banana", 2); + map.put("orange", 8); + map.put("grape", 1); + + Map sortedMap = CollectionUtils.sortByValue(map); + assertEquals(1, sortedMap.get("grape")); + assertEquals(2, sortedMap.get("banana")); + assertEquals(5, sortedMap.get("apple")); + assertEquals(8, sortedMap.get("orange")); + + // Test with empty map + Map emptyMap = new HashMap<>(); + Map sortedEmptyMap = CollectionUtils.sortByValue(emptyMap); + assertTrue(sortedEmptyMap.isEmpty()); + + // Test with null map + assertThrows(NullPointerException.class, () -> CollectionUtils.sortByValue(null)); + } +} diff --git a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java new file mode 100644 index 0000000..08e42ea --- /dev/null +++ b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/DateUtilsTest.java @@ -0,0 +1,201 @@ +package com.shortthirdman.primekit.essentials; + +import com.shortthirdman.primekit.essentials.common.util.DateUtils; + +import org.junit.jupiter.api.Test; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class DateUtilsTest { + + @Test + public void givenZonedDateTime_whenConvertToDate_thenCorrect() { + ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("UTC")); + Date date = DateUtils.convertToDate(zdt); + assertEquals(Date.from(zdt.toInstant()), date); + } + + @Test + public void givenDate_whenConvertToZonedDateTime_thenCorrect() { + Date date = new Date(); + ZoneId zoneId = ZoneId.of("UTC"); + ZonedDateTime zdt = DateUtils.convertToZonedDateTime(date, zoneId); + assertEquals(date.toInstant().atZone(zoneId), zdt); + } + + @Test + public void givenZonedDateTime_whenUsingLocalDateTime_thenConvertToTimestamp() { + ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 4, 17, 12, 30, 0, 0, ZoneId.systemDefault()); + Timestamp actualResult = DateUtils.convertToTimeStamp(zonedDateTime); + Timestamp expectedResult = Timestamp.valueOf("2024-04-17 12:30:00"); + assertEquals(expectedResult, actualResult); + } + + @Test + public void givenZonedDateTime_whenUsingInstant_thenConvertToTimestamp() { + ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 4, 17, 12, 30, 0, 0, ZoneId.systemDefault()); + Timestamp actualResult = DateUtils.convertToTimeStampFromInstant(zonedDateTime); + Timestamp expectedResult = Timestamp.valueOf("2024-04-17 12:30:00"); + assertEquals(expectedResult, actualResult); + } + + @Test + public void givenTimestamp_whenUsingLocalDateTime_thenConvertToZonedDateTime() { + Timestamp timestamp = Timestamp.valueOf("2024-04-17 12:30:00"); + ZonedDateTime actualResult = DateUtils.convertToZonedDateTimeFromLocalDateTime(timestamp); + ZonedDateTime expectedResult = ZonedDateTime.of(2024, 4, 17, 12, 30, 0, 0, ZoneId.systemDefault()); + assertEquals(expectedResult.toLocalDate(), actualResult.toLocalDate()); + assertEquals(expectedResult.toLocalTime(), actualResult.toLocalTime()); + } + + @Test + public void givenTimestamp_whenUsingCalendar_thenConvertToZonedDateTime() { + Timestamp timestamp = Timestamp.valueOf("2024-04-17 12:30:00"); + ZonedDateTime actualResult = DateUtils.convertToCalendarZonedDateTime(timestamp); + ZonedDateTime expectedResult = ZonedDateTime.of(2024, 4, 17, 12, 30, 0, 0, ZoneId.systemDefault()); + assertEquals(expectedResult.toLocalDate(), actualResult.toLocalDate()); + assertEquals(expectedResult.toLocalTime(), actualResult.toLocalTime()); + } + + @Test + public void givenTimestamp_whenUsingInstant_thenConvertToZonedDateTime() { + Timestamp timestamp = Timestamp.valueOf("2024-04-17 12:30:00"); + ZonedDateTime actualResult = DateUtils.convertToInstantZonedDateTime(timestamp); + ZonedDateTime expectedResult = ZonedDateTime.of(2024, 4, 17, 12, 30, 0, 0, ZoneId.systemDefault()); + assertEquals(expectedResult.toLocalDate(), actualResult.toLocalDate()); + assertEquals(expectedResult.toLocalTime(), actualResult.toLocalTime()); + } + + @Test + public void givenLocalDate_isWeekend_thenTrue() { + LocalDate someDate = LocalDate.of(2021, 1, 2); // 2nd-Jan-2021 + assertTrue(DateUtils.isWeekend(someDate)); + } + + @Test + public void givenDate_isWeekend_thenTrue() { + Date someDate = new Date(2021, 0, 2); + assertTrue(DateUtils.isWeekend(someDate)); + } + + @Test + public void givenLocalDate_addBusinessDays() { + LocalDate today = LocalDate.of(2020, 5, 5); + + List holidays = new ArrayList<>(); + holidays.add(LocalDate.of(2020, 5, 11)); + holidays.add(LocalDate.of(2020, 5, 1)); + + LocalDate expectedDate1 = LocalDate.of(2020, 5, 15); + LocalDate actualDate1 = DateUtils.addBusinessDays(today, 8, List.of()); + assertEquals(expectedDate1, actualDate1, "Dates are matching"); + + LocalDate expectedDate2 = LocalDate.of(2020, 5, 18); + LocalDate actualDate2 = DateUtils.addBusinessDays(today, 8, holidays); + assertEquals(expectedDate2, actualDate2, "Dates are matching"); + } + + @Test + public void givenLocalDate_subtractBusinessDays() { + LocalDate today = LocalDate.of(2020, 5, 5); + + List holidays = new ArrayList<>(); + holidays.add(LocalDate.of(2020, 5, 11)); + holidays.add(LocalDate.of(2020, 5, 1)); + + LocalDate expectedDate1 = LocalDate.of(2020, 4, 22); + LocalDate actualDate1 = DateUtils.subtractBusinessDays(today, 8, List.of()); + assertEquals(expectedDate1, actualDate1, "Dates are matching"); + + LocalDate expectedDate2 = LocalDate.of(2020, 4, 21); + LocalDate actualDate2 = DateUtils.subtractBusinessDays(today, 8, holidays); + assertEquals(expectedDate2, actualDate2, "Dates are matching"); + } + + @Test + public void givenStartDate_givenEndDate_datesBetween() { + LocalDate startDate = LocalDate.now(); + LocalDate endDate = startDate.plusMonths(2); + + List dateList = DateUtils.datesBetween(startDate, endDate); + assertEquals(61, dateList.size()); + assertNotEquals(List.of(), dateList); + } + + @Test + public void givenDateString_parseStrictly() { + LocalDate expectedValue1 = LocalDate.of(2019, 2, 27); + assertEquals(expectedValue1, DateUtils.strictParseDate("2019-02-27", "yyyy-MM-dd")); + + LocalDate expectedValue2 = LocalDate.of(2019, 2, 28); + assertEquals(expectedValue2, DateUtils.strictParseDate("2019-02-28", "yyyy-MM-dd")); + + LocalDate expectedValue3 = LocalDate.of(2019, 2, 29); + assertEquals(expectedValue3, DateUtils.strictParseDate("2019-02-29", "yyyy-MM-dd")); + } + + @Test + public void givenDateTimeString_parseStrictly() { + LocalDateTime expectedValue1 = LocalDateTime.of(2019, 2, 27, 11, 23, 56, 1234); + assertEquals(expectedValue1, DateUtils.strictParseDateTime("2019-02-27T11:23:56.1234", null)); + + LocalDateTime expectedValue2 = LocalDateTime.of(2019, 2, 27, 11, 23, 56, 1234); + assertEquals(expectedValue2, DateUtils.strictParseDateTime("2019-02-28T11:23:56.1234", null)); + + LocalDateTime expectedValue3 = LocalDateTime.of(2019, 2, 27, 11, 23, 56, 1234); + assertEquals(expectedValue3, DateUtils.strictParseDateTime("2019-02-29T11:23:56.1234", null)); + } + + @Test + public void givenMonthNumber_monthShortName() { + assertEquals("Jan", DateUtils.monthNumberToShortName(1)); + assertNotEquals("JAN", DateUtils.monthNumberToShortName(1)); + assertNotNull(DateUtils.monthNumberToShortName(1)); + + assertEquals("Jun", DateUtils.monthNumberToShortName(6)); + assertNotEquals("JUN", DateUtils.monthNumberToShortName(6)); + assertNotNull(DateUtils.monthNumberToShortName(6)); + } + + @Test + public void givenMonthNumber_monthFullName() { + assertEquals("January", DateUtils.monthNumberToFullName(1)); + assertNotEquals("JANUARY", DateUtils.monthNumberToFullName(1)); + assertNotNull(DateUtils.monthNumberToFullName(1)); + } + + @Test + public void givenMonthNumber_monthName() { + assertEquals("JANUARY", DateUtils.monthNumberToName(1)); + assertNotEquals("January", DateUtils.monthNumberToName(1)); + assertNotNull(DateUtils.monthNumberToName(1)); + } + + @Test + public void givenMonthShortName_convertMonthNumber() { + assertEquals(1, DateUtils.monthShortNameToNumber("Jan")); + assertThrows(IllegalArgumentException.class, () -> DateUtils.monthShortNameToNumber("Duy")); + } + + @Test + public void givenMonthFullName_convertMonthNumber() { + assertEquals(1, DateUtils.monthNameToNumber("January")); + } + + @Test + public void givenMonthShortName_convertFullName() { + assertEquals("January", DateUtils.monthShortNameToFullName("Jan")); + assertThrows(IllegalArgumentException.class, () -> DateUtils.monthShortNameToFullName("Fut")); + } +} diff --git a/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/MathUtilsTests.java b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/MathUtilsTests.java new file mode 100644 index 0000000..642bf4f --- /dev/null +++ b/primekit-essentials/src/test/java/com/shortthirdman/primekit/essentials/MathUtilsTests.java @@ -0,0 +1,73 @@ +package com.shortthirdman.primekit.essentials; + +import com.shortthirdman.primekit.essentials.common.util.MathUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MathUtilsTests { + + @Test + void testVariance() { + double[] array = {1, 2, 3, 4, 5}; + int n = array.length; + assertEquals(2.5, MathUtils.variance(array, n)); + } + + @Test + void testStandardDeviation() { + double[] array = {1, 2, 3, 4, 5}; + int n = array.length; + assertEquals(1.5811388300841898, MathUtils.standardDeviation(array, n)); + } + + @Test + void testAverage() { + double[] array = {1, 2, 3, 4, 5}; + int n = array.length; + assertEquals(3.0, MathUtils.average(array, n)); + } + + @Test + void testVarianceWithEmptyArray() { + double[] array = {}; + int n = array.length; + assertThrows(IllegalArgumentException.class, () -> MathUtils.variance(array, n)); + } + + @Test + void testStandardDeviationWithEmptyArray() { + double[] array = {}; + int n = array.length; + assertThrows(IllegalArgumentException.class, () -> MathUtils.standardDeviation(array, n)); + } + + @Test + void testAverageWithEmptyArray() { + double[] array = {}; + int n = array.length; + assertThrows(IllegalArgumentException.class, () -> MathUtils.average(array, n)); + } + + @Test + void testVarianceWithSingleElement() { + double[] array = {5}; + int n = array.length; + assertEquals(0.0, MathUtils.variance(array, n)); + } + + @Test + void testStandardDeviationWithSingleElement() { + double[] array = {5}; + int n = array.length; + assertEquals(0.0, MathUtils.standardDeviation(array, n)); + } + + @Test + void testAverageWithSingleElement() { + double[] array = {5}; + int n = array.length; + assertEquals(5.0, MathUtils.average(array, n)); + } +}