From 5256bd99b774771f4eb68c20fb9a8d5f19cb6ea6 Mon Sep 17 00:00:00 2001 From: arisnguyenit97 Date: Sun, 12 May 2024 20:51:09 +0700 Subject: [PATCH] :sparkles: feat: add unify functions transform4j vi4j random4j #4 --- plugin/build.gradle | 2 + .../groovy/org/unify4j/common/Random4j.java | 221 ++++++++++++++++++ .../org/unify4j/common/Transform4j.java | 27 +++ .../main/groovy/org/unify4j/common/Vi4j.java | 159 +++++++++++++ 4 files changed, 409 insertions(+) create mode 100644 plugin/src/main/groovy/org/unify4j/common/Transform4j.java create mode 100644 plugin/src/main/groovy/org/unify4j/common/Vi4j.java diff --git a/plugin/build.gradle b/plugin/build.gradle index 042611b..9b83e9c 100755 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -77,4 +77,6 @@ dependencies { // The "json-path" library, version 2.9.0, facilitates easy manipulation and querying of JSON data structures in Java applications, // offering powerful tools for navigating and extracting data from JSON documents. implementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0' + // The "validation-api" library, version 2.0.1.Final, provides tools for validating Java objects according to defined constraints. + implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' } diff --git a/plugin/src/main/groovy/org/unify4j/common/Random4j.java b/plugin/src/main/groovy/org/unify4j/common/Random4j.java index 8efdec1..892be57 100644 --- a/plugin/src/main/groovy/org/unify4j/common/Random4j.java +++ b/plugin/src/main/groovy/org/unify4j/common/Random4j.java @@ -3,6 +3,227 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Random; +import java.util.UUID; + public class Random4j { protected static final Logger logger = LoggerFactory.getLogger(Random4j.class); + + /** + *

+ * Returns a random integer within the specified range. + *

+ * + * @param startInclusive the smallest value that can be returned, must be non-negative + * @param endExclusive the upper bound (not included) + * @return the random integer + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if + * {@code startInclusive} is negative + */ + public static int nextInt(final int startInclusive, final int endExclusive) { + Vi4j.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Vi4j.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endExclusive) { + return startInclusive; + } + return startInclusive + new Random().nextInt(endExclusive - startInclusive); + } + + /** + *

Returns a random int within 0 - Integer.MAX_VALUE

+ * + * @return the random integer + * @see #nextInt(int, int) + */ + public static int nextInt() { + return nextInt(0, Integer.MAX_VALUE); + } + + /** + *

+ * Returns a random long within the specified range. + *

+ * + * @param startInclusive the smallest value that can be returned, must be non-negative + * @param endExclusive the upper bound (not included) + * @return the random long + * @throws IllegalArgumentException if {@code startInclusive > endExclusive} or if + * {@code startInclusive} is negative + */ + public static long nextLong(final long startInclusive, final long endExclusive) { + Vi4j.isTrue(endExclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Vi4j.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endExclusive) { + return startInclusive; + } + return (long) nextDouble(startInclusive, endExclusive); + } + + /** + *

+ * Returns a random double within the specified range. + *

+ * + * @param startInclusive the smallest value that can be returned, must be non-negative + * @param endInclusive the upper bound (included) + * @return the random double + * @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if + * {@code startInclusive} is negative + */ + public static double nextDouble(final double startInclusive, final double endInclusive) { + Vi4j.isTrue(endInclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Vi4j.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endInclusive) { + return startInclusive; + } + return startInclusive + ((endInclusive - startInclusive) * new Random().nextDouble()); + } + + /** + *

Returns a random double within 0 - Double.MAX_VALUE

+ * + * @return the random double + * @see #nextDouble(double, double) + */ + public static double nextDouble() { + return nextDouble(0, Double.MAX_VALUE); + } + + /** + *

+ * Returns a random float within the specified range. + *

+ * + * @param startInclusive the smallest value that can be returned, must be non-negative + * @param endInclusive the upper bound (included) + * @return the random float + * @throws IllegalArgumentException if {@code startInclusive > endInclusive} or if + * {@code startInclusive} is negative + */ + public static float nextFloat(final float startInclusive, final float endInclusive) { + Vi4j.isTrue(endInclusive >= startInclusive, "Start value must be smaller or equal to end value."); + Vi4j.isTrue(startInclusive >= 0, "Both range values must be non-negative."); + if (startInclusive == endInclusive) { + return startInclusive; + } + return startInclusive + ((endInclusive - startInclusive) * new Random().nextFloat()); + } + + /** + *

Returns a random float within 0 - Float.MAX_VALUE

+ * + * @return the random float + * @see #nextFloat() + */ + public static float nextFloat() { + return nextFloat(0, Float.MAX_VALUE); + } + + /** + * Generates a random 4-digit OTP. + * + * @return A random 4-digit OTP. + */ + public static int nextOTP() { + return (int) ((Math.random() * 9000) + 1000); + } + + /** + * Generates a random 4-digit OTP as a string. + * + * @return A string representing a random 4-digit OTP. + */ + public static String nextOTPString() { + return String.valueOf(nextOTP()); + } + + /** + * Generates a UUID based on current time and random values. + * + * @return A UUID. + */ + public static UUID nextUUID() { + long most64SigBits = get64MostSignificantBit(); + long least64SigBits = get64LeastSignificantBit(); + return new UUID(most64SigBits, least64SigBits); + } + + /** + * Generates a string representation of a UUID based on current time and random values. + * + * @return A string representation of a UUID. + */ + @SuppressWarnings({"DuplicatedCode", "AccessStaticViaInstance"}) + public static String nextUUIDString() { + return nextUUID().randomUUID().toString().replaceAll("-", ""); + } + + /** + * Generates a random password of length equal to {@code length}, + * consisting only of the characters contained in {@code combination}. + * + *

If {@code combination} contains more than one occurrence of a character, + * the overall probability of using it in password generation will be higher. + * + * @param length - the desired password length. + * @param chars - the letter set used in the generation process. + * @return the generated password. + */ + public static String nextPassword(int length, char[] chars) { + StringBuilder builder = new StringBuilder(length); + Random random = new Random(); + + for (int i = 0, n = chars.length; i < length; i++) { + builder.append(chars[random.nextInt(n)]); + } + + return builder.toString(); + } + + /** + * Generates a random password of length equal to {@code length}, + * consisting only of the characters contained in {@code combination}. + * + *

If {@code combination} contains more than one occurrence of a character, + * the overall probability of using it in password generation will be higher. + * + * @param length - the desired password length. + * @param combination - the letter set used in the generation process. + * @return the generated password. + */ + public static String nextPassword(int length, String combination) { + char[] chars = combination.toCharArray(); + return nextPassword(length, chars); + } + + /** + * Generates a random password of 8 characters + * + * @param length - the desired password length. + * @return The randomized password as a String + */ + public static String nextPassword(int length) { + String[] values = {"a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "z", "x", "y", "1", "2", "3", "4", "5", "6", "7", "8", "9", "&", "%", "?", "!", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "Z", "X", "Y"}; + return nextPassword(length, Transform4j.fromArray2Chars(values)); + } + + private static long get64LeastSignificantBit() { + Random random = new Random(); + long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL; + long variant3BitFlag = 0x8000000000000000L; + return random63BitLong + variant3BitFlag; + } + + private static long get64MostSignificantBit() { + LocalDateTime start = LocalDateTime.of(1900, 10, 15, 0, 0, 0); + Duration duration = Duration.between(start, LocalDateTime.now()); + long seconds = duration.getSeconds(); + long nanos = duration.getNano(); + long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100; + long least12SignificantBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4; + long version = 1 << 12; + return (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificantBitOfTime; + } } diff --git a/plugin/src/main/groovy/org/unify4j/common/Transform4j.java b/plugin/src/main/groovy/org/unify4j/common/Transform4j.java new file mode 100644 index 0000000..2ca467d --- /dev/null +++ b/plugin/src/main/groovy/org/unify4j/common/Transform4j.java @@ -0,0 +1,27 @@ +package org.unify4j.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Transform4j { + protected static final Logger logger = LoggerFactory.getLogger(Transform4j.class); + + /** + * Converts an array of strings to a single character array. + * + * @param arrays The array of strings to be converted. + * @return A character array containing the characters from the input strings. + */ + public static char[] fromArray2Chars(String[] arrays) { + if (arrays == null || arrays.length == 0) { + return new char[0]; + } + StringBuilder builder = new StringBuilder(); + for (String s : arrays) { + if (s != null) { + builder.append(s); + } + } + return builder.toString().toCharArray(); + } +} diff --git a/plugin/src/main/groovy/org/unify4j/common/Vi4j.java b/plugin/src/main/groovy/org/unify4j/common/Vi4j.java new file mode 100644 index 0000000..9204a50 --- /dev/null +++ b/plugin/src/main/groovy/org/unify4j/common/Vi4j.java @@ -0,0 +1,159 @@ +package org.unify4j.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.validation.*; +import java.util.Set; + +public class Vi4j { + protected static final Logger logger = LoggerFactory.getLogger(Vi4j.class); + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);
+ * + *

For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final long value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, value)); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);
+ * + *

For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final double value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, value)); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max);
+     * Validate.isTrue(myObject.isOk(), "The object is not okay");
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + */ + public static void isTrue(final boolean expression, final String message, final Object... values) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i > 0);
+     * Validate.isTrue(myObject.isOk());
+ * + *

The message of the exception is "The validated expression is + * false".

+ * + * @param expression the boolean expression to check + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression) { + if (!expression) { + throw new IllegalArgumentException("The validated expression is false"); + } + } + + /** + * Retrieves the default validator factory and creates a validator. + * + * @return The default validator instance. + */ + public static Validator defaultFactory() { + try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { + return factory.getValidator(); + } + } + + /** + * Validates the provided request object using the specified validator. + * Throws an exception if any constraint violations are found. + * + * @param the type of the request object + * @param request the request object to validate + * @param validator the validator to use for validation + * @throws IllegalArgumentException if the request object is null + * @throws ConstraintViolationException if any constraint violations are found + */ + public static void validate(T request, Validator validator) { + if (request == null) { + throw new IllegalArgumentException("Class is required"); + } + Set> violations = validator.validate(request); + if (!violations.isEmpty()) { + StringBuilder builder = new StringBuilder(); + for (ConstraintViolation constraintViolation : violations) { + builder.append(constraintViolation.getMessage()); + builder.append(", "); + } + throw new ConstraintViolationException("Invalid fields: " + builder, violations); + } + } + + /** + * Validates the provided request object using the specified validator. + * Throws an exception if any constraint violations are found. + * + * @param the type of the request object + * @param request the request object to validate + * @throws IllegalArgumentException if the request object is null + * @throws ConstraintViolationException if any constraint violations are found + */ + public static void validate(T request) { + validate(request, defaultFactory()); + } +}