Skip to content

Commit 1888ab2

Browse files
authored
AYS-618 | @OnlyInteger Validation Annotation Has Been Created and Annotation Has Been Used for AysUserFilter.lineNumber, EmergencyEvacuationApplicationFilter.referenceNumber Fields (#478)
1 parent 79d53a3 commit 1888ab2

File tree

8 files changed

+278
-102
lines changed

8 files changed

+278
-102
lines changed

src/main/java/org/ays/auth/model/AysUserFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import org.ays.auth.model.enums.AysUserStatus;
1111
import org.ays.common.model.AysFilter;
1212
import org.ays.common.util.validation.Name;
13-
import org.ays.common.util.validation.OnlyPositiveNumber;
13+
import org.ays.common.util.validation.OnlyInteger;
1414
import org.springframework.data.jpa.domain.Specification;
1515
import org.springframework.util.StringUtils;
1616

@@ -66,7 +66,7 @@ public class AysUserFilter implements AysFilter {
6666
@Setter
6767
public static class PhoneNumber {
6868

69-
@OnlyPositiveNumber
69+
@OnlyInteger(sign = OnlyInteger.Sign.POSITIVE)
7070
@Size(min = 1, max = 10)
7171
private String lineNumber;
7272

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.ays.common.util.validation;
2+
3+
import jakarta.validation.Constraint;
4+
import jakarta.validation.Payload;
5+
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
11+
/**
12+
* Annotation to validate that a numeric field is an integer and matches a specific sign constraint
13+
* (e.g., positive, negative).
14+
* <p>
15+
* This constraint is validated using the {@link OnlyIntegerValidator} class.
16+
* It can be applied to fields of numeric-compatible types such as {@link String}, {@link Integer}, or {@link Long}.
17+
* The value must be a valid integer string and conform to the defined {@link Sign} rule.
18+
* </p>
19+
*
20+
* <p><strong>Example usage:</strong></p>
21+
* <pre>
22+
* {@code
23+
* @OnlyInteger(sign = OnlyInteger.Sign.POSITIVE)
24+
* private String age;
25+
* }
26+
* </pre>
27+
*/
28+
@Target(ElementType.FIELD)
29+
@Retention(RetentionPolicy.RUNTIME)
30+
@Constraint(validatedBy = OnlyIntegerValidator.class)
31+
public @interface OnlyInteger {
32+
33+
/**
34+
* Defines the custom error message to be returned when validation fails.
35+
*
36+
* @return the validation error message
37+
*/
38+
String message() default "must be integer";
39+
40+
/**
41+
* Specifies the expected sign of the validated number.
42+
* <ul>
43+
* <li>{@link Sign#POSITIVE} - value must be greater than 0</li>
44+
* <li>{@link Sign#NEGATIVE} - value must be less than 0</li>
45+
* <li>{@link Sign#ANY} - value can be positive, negative, or zero (default)</li>
46+
* </ul>
47+
*
48+
* @return the expected sign constraint
49+
*/
50+
Sign sign() default Sign.ANY;
51+
52+
/**
53+
* Enumeration representing supported sign constraints for integer validation.
54+
*/
55+
enum Sign {
56+
57+
/**
58+
* Indicates that the number must be a positive integer (> 0).
59+
*/
60+
POSITIVE,
61+
62+
/**
63+
* Indicates that the number must be a negative integer (< 0).
64+
*/
65+
NEGATIVE,
66+
67+
/**
68+
* Indicates that the number can be of any sign (positive, negative, or zero).
69+
*/
70+
ANY;
71+
72+
/**
73+
* Checks if this sign type requires the value to be positive.
74+
*
75+
* @return true if the sign is {@code POSITIVE}, false otherwise
76+
*/
77+
public boolean isPositive() {
78+
return this == POSITIVE;
79+
}
80+
81+
/**
82+
* Checks if this sign type requires the value to be negative.
83+
*
84+
* @return true if the sign is {@code NEGATIVE}, false otherwise
85+
*/
86+
public boolean isNegative() {
87+
return this == NEGATIVE;
88+
}
89+
}
90+
91+
/**
92+
* Defines the validation groups that this constraint belongs to.
93+
*
94+
* @return the validation groups
95+
*/
96+
Class<?>[] groups() default {};
97+
98+
/**
99+
* Allows clients of the Bean Validation API to assign custom payload objects to this constraint.
100+
*
101+
* @return the custom payload objects
102+
*/
103+
Class<? extends Payload>[] payload() default {};
104+
105+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package org.ays.common.util.validation;
2+
3+
import jakarta.validation.ConstraintValidator;
4+
import jakarta.validation.ConstraintValidatorContext;
5+
import org.apache.commons.lang3.StringUtils;
6+
import org.apache.commons.lang3.math.NumberUtils;
7+
8+
/**
9+
* A custom validator implementation for the {@link OnlyInteger} annotation.
10+
* <p>
11+
* Validates whether a given {@link String} value conforms to the expected sign constraint
12+
* ({@code POSITIVE}, {@code NEGATIVE}, or {@code ANY}) and represents a valid integer value.
13+
* </p>
14+
*
15+
* <p><strong>Validation behavior:</strong></p>
16+
* <ul>
17+
* <li>If the input is {@code null} or blank, it is considered valid.</li>
18+
* <li>The input must be an integer value (no decimal points or non-numeric characters).</li>
19+
* <li>The sign of the number must match the configured {@link OnlyInteger.Sign} rule.</li>
20+
* <li>Custom messages such as {@code "must be positive integer"} or {@code "must be negative integer"} are added on failure.</li>
21+
* </ul>
22+
*/
23+
class OnlyIntegerValidator implements ConstraintValidator<OnlyInteger, String> {
24+
25+
private OnlyInteger.Sign sign;
26+
27+
/**
28+
* Initializes the validator with the configured sign constraint from the {@link OnlyInteger} annotation.
29+
*
30+
* @param constraintAnnotation the annotation instance containing the configuration
31+
*/
32+
@Override
33+
public void initialize(OnlyInteger constraintAnnotation) {
34+
this.sign = constraintAnnotation.sign();
35+
}
36+
37+
/**
38+
* Validates whether the given string is a valid integer and conforms to the specified sign constraint.
39+
*
40+
* @param number the value to validate
41+
* @param context the constraint validation context
42+
* @return {@code true} if the number is valid or blank; {@code false} otherwise
43+
*/
44+
@Override
45+
public boolean isValid(String number, ConstraintValidatorContext context) {
46+
47+
if (StringUtils.isEmpty(number)) {
48+
return true;
49+
}
50+
51+
if (this.isValidInteger(number)) {
52+
return false;
53+
}
54+
55+
return this.isPositiveOrNegativeInteger(number, context);
56+
}
57+
58+
/**
59+
* Checks whether the given string can be parsed as an integer.
60+
*
61+
* @param number the input string
62+
* @return {@code true} if not an integer (i.e., invalid); {@code false} if it is parsable
63+
*/
64+
private boolean isValidInteger(String number) {
65+
66+
if (!NumberUtils.isParsable(number)) {
67+
return true;
68+
}
69+
70+
return number.contains(".");
71+
}
72+
73+
/**
74+
* Validates the integer's sign according to the specified {@link org.ays.common.util.validation.OnlyInteger.Sign}.
75+
*
76+
* @param number the string representing an integer
77+
* @param context the validation context
78+
* @return {@code true} if the sign is valid; {@code false} otherwise
79+
*/
80+
private boolean isPositiveOrNegativeInteger(String number, ConstraintValidatorContext context) {
81+
82+
if (sign.isPositive()) {
83+
context.disableDefaultConstraintViolation();
84+
context.buildConstraintViolationWithTemplate("must be positive integer")
85+
.addConstraintViolation();
86+
return this.isPositive(number);
87+
}
88+
89+
if (sign.isNegative()) {
90+
context.disableDefaultConstraintViolation();
91+
context.buildConstraintViolationWithTemplate("must be negative integer")
92+
.addConstraintViolation();
93+
return this.isNegative(number);
94+
}
95+
96+
return true;
97+
}
98+
99+
/**
100+
* Checks whether the given string represents a positive integer.
101+
*
102+
* @param number the input string
103+
* @return {@code true} if the number is greater than zero
104+
*/
105+
private boolean isPositive(String number) {
106+
107+
if (!NumberUtils.isDigits(number)) {
108+
return false;
109+
}
110+
111+
if (number.length() > Long.toString(Long.MAX_VALUE).length()) {
112+
return true;
113+
}
114+
115+
return Long.parseLong(number) > 0;
116+
}
117+
118+
/**
119+
* Checks whether the given string represents a negative integer.
120+
*
121+
* @param number the input string
122+
* @return {@code true} if the number is lower than zero
123+
*/
124+
private boolean isNegative(String number) {
125+
return !number.startsWith("-") && Long.parseLong(number) < 0;
126+
}
127+
128+
}

src/main/java/org/ays/common/util/validation/OnlyPositiveNumber.java

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/main/java/org/ays/common/util/validation/OnlyPositiveNumberValidator.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/main/java/org/ays/emergency_application/model/filter/EmergencyEvacuationApplicationFilter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.apache.commons.collections4.CollectionUtils;
88
import org.ays.common.model.AysFilter;
99
import org.ays.common.util.validation.NoSpecialCharacters;
10+
import org.ays.common.util.validation.OnlyInteger;
1011
import org.ays.emergency_application.model.entity.EmergencyEvacuationApplicationEntity;
1112
import org.ays.emergency_application.model.enums.EmergencyEvacuationApplicationStatus;
1213
import org.hibernate.validator.constraints.Range;
@@ -19,6 +20,7 @@
1920
@Builder
2021
public class EmergencyEvacuationApplicationFilter implements AysFilter {
2122

23+
@OnlyInteger(sign = OnlyInteger.Sign.POSITIVE)
2224
@Size(min = 1, max = 10)
2325
private String referenceNumber;
2426

@@ -51,8 +53,6 @@ public class EmergencyEvacuationApplicationFilter implements AysFilter {
5153
/**
5254
* Converts this request's filter configuration into a {@link Specification} for querying.
5355
*
54-
* @param clazz The class type to which the specification will be applied.
55-
* @param <C> The type of the class.
5656
* @return A specification built based on the current filter configuration.
5757
*/
5858
@Override

0 commit comments

Comments
 (0)