Skip to content

HV-1970 Add Korean specific RRN annotation #1338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bafee67
HV-1970 Adding KorRRN annotation
ing9990 Jan 12, 2024
e4d3227
HV-1970 Adding KorRRN validator tests
ing9990 Jan 12, 2024
123f252
HV-1970 Refactoring to the Hibernate Code Style
ing9990 Jan 12, 2024
a672e04
HV-1970 Remove type field
ing9990 Jan 12, 2024
98a5164
HV-1970 Encoding Javadoc url
ing9990 Jan 14, 2024
ec01653
HV-1970 Adding RRN validation criteria and modifying test code
ing9990 Jan 15, 2024
883f3ef
HV-1970 Encoding javadoc url
ing9990 Jan 15, 2024
103eac2
HV-1970 Adding test cases and refactoring the RRNValidator
ing9990 Jan 15, 2024
00ddc91
HV-1970 fix error
ing9990 Jan 15, 2024
a3f45bc
HV-1970 Add attribute to optionally validate check-digit, adding and …
ing9990 Jan 16, 2024
f60784f
HV-1970 Remove private signature for Java8 version
ing9990 Jan 16, 2024
d0962dc
HV-1970 Change 'if' statement to 'switch' statement for Readability,…
ing9990 Jan 16, 2024
5f1337b
HV-1970 Change 'switch' statement to 'if' statement for Java 11
ing9990 Jan 16, 2024
f8e4c09
HV-1970 Change attribute name from BEFORE_OCTOBER_2020_ONLY to ALWAYS
ing9990 Jan 16, 2024
c625080
HV-1970 Adding test case for default value of validateCheckDigit()
ing9990 Jan 24, 2024
9566047
HV-1970 Fix typos
ing9990 Jan 24, 2024
7962786
HV-1970 Refactor: Moving validation methods to RRNValidationAlgorithm…
ing9990 Jan 24, 2024
b8951fb
HV-1970 Fix typo
ing9990 Jan 24, 2024
c589eb0
HV-1970 Adding asciidoc for KorRRN Annotation
ing9990 Jan 24, 2024
974c616
HV-1970 Modifying test case to pass the test
ing9990 Jan 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ public ConstraintHelper(Types typeUtils, AnnotationApiHelper annotationApiHelper
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NIP_CHECK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.PESEL_CHECK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.INN_CHECK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.KOR_RRN_CHECK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_BLANK, CharSequence.class );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NOT_EMPTY, TYPES_SUPPORTED_BY_SIZE_AND_NOT_EMPTY_ANNOTATIONS );
registerAllowedTypesForBuiltInConstraint( HibernateValidatorTypes.NORMALIZED, CharSequence.class );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public static class HibernateValidatorTypes {
public static final String NIP_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.NIP";
public static final String PESEL_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".pl.PESEL";
public static final String INN_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".ru.INN";
public static final String KOR_RRN_CHECK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + "kor.KorRRN";
public static final String NORMALIZED = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".Normalized";
public static final String UUID = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".UUID";
public static final String NOT_BLANK = ORG_HIBERNATE_VALIDATOR_CONSTRAINTS + ".NotBlank";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.defs.kor;

import org.hibernate.validator.cfg.ConstraintDef;
import org.hibernate.validator.constraints.kor.KorRRN;

public class KorRRNDef extends ConstraintDef<KorRRNDef, KorRRN> {

public KorRRNDef() {
super( KorRRN.class );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/

/**
* <p>Korean specific constraint definition classes for programmatic constraint definition API.</p>
* <p>This package is part of the public Hibernate Validator API.</p>
*/
package org.hibernate.validator.cfg.defs.kor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.constraints.kor;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Checks that the annotated character sequence is a valid Korean resident registration number.
*
* @author Taewoo Kim
* @see <a href="https://ko.wikipedia.org/wiki/%EC%A3%BC%EB%AF%BC%EB%93%B1%EB%A1%9D%EB%B2%88%ED%98%B8">Korean resident registration number</a>
*/

@Documented
@Constraint(validatedBy = {})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface KorRRN {

String message() default "{org.hibernate.validator.constraints.kor.KorRRN.message}";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.internal.constraintvalidators.hv.kor;

import java.util.regex.Pattern;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import org.hibernate.validator.constraints.kor.KorRRN;

public class KorRRNValidator implements ConstraintValidator<KorRRN, CharSequence> {

private static final Pattern REGEX = Pattern.compile( "\\d{6}-\\d{7}" );

private static final int CHECK_DIGIT_INDEX = 13;
private static final int[] CHECK_DIGIT_ARRAY = { 2, 3, 4, 5, 6, 7, -1, 8, 9, 2, 3, 4, 5 };

@Override
public boolean isValid(CharSequence rrnValue, ConstraintValidatorContext context) {
if ( rrnValue == null ) {
return true;
}
String rrn = rrnValue.toString();

if ( !isValidFormat( rrn ) ) {
return false;
}

int month = Integer.parseInt( rrn.substring( 2, 4 ) );
int day = Integer.parseInt( rrn.substring( 4, 6 ) );

if ( day > 31 || ( day > 30 && ( month == 4 || month == 6 || month == 9 || month == 11 ) ) || ( day > 28 && month == 2 ) || rrn.length() != 14 ) {
return false;
}

return validateCheckSum( rrn.toCharArray(), rrnValue.charAt( CHECK_DIGIT_INDEX ) );
}

private static boolean isValidFormat(String rrn) {
return REGEX.matcher( rrn ).matches();
}

private boolean validateCheckSum(char[] rrnArray, char code) {
int sum = 0;
for ( int i = 0; i < CHECK_DIGIT_ARRAY.length; i++ ) {
if ( i == 6 ) {
continue;
}
sum += CHECK_DIGIT_ARRAY[i] * Character.getNumericValue( rrnArray[i] );
}

return ( 11 - sum % 11 ) % 10 == Character.getNumericValue( code );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ enum BuiltinConstraint {
Arrays.asList( JAKARTA_VALIDATION_CONSTRAINTS_PATTERN )),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_BR_TITULO_ELEITORAL("org.hibernate.validator.constraints.br.TituloEleitoral",
Arrays.asList( JAKARTA_VALIDATION_CONSTRAINTS_PATTERN, ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_MOD11_CHECK ) ),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_KOR_KORRRN("org.hibernate.validator.constraints.kor.KorRRN"),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_PL_NIP("org.hibernate.validator.constraints.pl.NIP"),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_PL_PESEL("org.hibernate.validator.constraints.pl.PESEL"),
ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_PL_REGON("org.hibernate.validator.constraints.pl.REGON"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_EAN;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_EMAIL;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_ISBN;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_KOR_KORRRN;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_LENGTH;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_LUHN_CHECK;
import static org.hibernate.validator.internal.metadata.core.BuiltinConstraint.ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_MOD10_CHECK;
Expand Down Expand Up @@ -99,6 +100,7 @@
import org.hibernate.validator.constraints.br.CNPJ;
import org.hibernate.validator.constraints.br.CPF;
import org.hibernate.validator.constraints.br.TituloEleitoral;
import org.hibernate.validator.constraints.kor.KorRRN;
import org.hibernate.validator.constraints.pl.NIP;
import org.hibernate.validator.constraints.pl.PESEL;
import org.hibernate.validator.constraints.pl.REGON;
Expand Down Expand Up @@ -317,6 +319,7 @@
import org.hibernate.validator.internal.constraintvalidators.hv.UniqueElementsValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.br.CNPJValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.br.CPFValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.kor.KorRRNValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.pl.NIPValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.pl.PESELValidator;
import org.hibernate.validator.internal.constraintvalidators.hv.pl.REGONValidator;
Expand Down Expand Up @@ -801,6 +804,9 @@ private ConstraintHelper(Set<BuiltinConstraint> enabledBuiltinConstraints) {
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_RANGE ) ) {
putBuiltinConstraint( tmpConstraints, Range.class );
}
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_KOR_KORRRN ) ) {
putBuiltinConstraint( tmpConstraints, KorRRN.class, KorRRNValidator.class );
}
if ( enabledBuiltinConstraints.contains( ORG_HIBERNATE_VALIDATOR_CONSTRAINTS_PL_REGON ) ) {
putBuiltinConstraint( tmpConstraints, REGON.class, REGONValidator.class );
}
Expand Down Expand Up @@ -1174,7 +1180,7 @@ private static <T> T run(PrivilegedAction<T> action) {

/**
* A type-safe wrapper around a concurrent map from constraint types to
* associated validator classes. The casts are safe as data is added trough
* associated validator classes. The casts are safe as data is added through
* the typed API only.
*
* @author Gunnar Morling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ org.hibernate.validator.constraints.br.CNPJ.message = invalid Br
org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF)
org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number

org.hibernate.validator.constraints.kor.KorRRN.message = invalid Korean resident registration number (KorRRN)

org.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON)
org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP)
org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.hibernate.validator.cfg.defs.br.CNPJDef;
import org.hibernate.validator.cfg.defs.br.CPFDef;
import org.hibernate.validator.cfg.defs.br.TituloEleitoralDef;
import org.hibernate.validator.cfg.defs.kor.KorRRNDef;
import org.hibernate.validator.cfg.defs.pl.NIPDef;
import org.hibernate.validator.cfg.defs.pl.PESELDef;
import org.hibernate.validator.cfg.defs.pl.REGONDef;
Expand All @@ -32,6 +33,7 @@
import org.hibernate.validator.constraints.br.CNPJ;
import org.hibernate.validator.constraints.br.CPF;
import org.hibernate.validator.constraints.br.TituloEleitoral;
import org.hibernate.validator.constraints.kor.KorRRN;
import org.hibernate.validator.constraints.pl.NIP;
import org.hibernate.validator.constraints.pl.PESEL;
import org.hibernate.validator.constraints.pl.REGON;
Expand Down Expand Up @@ -64,6 +66,9 @@ public void countrySpecificProgrammaticDefinition() {

doProgrammaticTest( INN.class, new INNDef().type( INN.Type.INDIVIDUAL ), "127530851622", "127530851623", "invalid Russian taxpayer identification number (INN)" );
doProgrammaticTest( INN.class, new INNDef().type( INN.Type.JURIDICAL ), "8606995694", "8606995695", "invalid Russian taxpayer identification number (INN)" );

doProgrammaticTest( KorRRN.class, new KorRRNDef(), "030205-1000004", "010101-1063015", "invalid Korean resident registration number (KorRRN)" );
doProgrammaticTest( KorRRN.class, new KorRRNDef(), "030205-2567485", "030299-5000000", "invalid Korean resident registration number (KorRRN)" );
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.constraints.annotations.hv.kor;

import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf;

import jakarta.validation.ConstraintViolation;

import java.util.Set;

import org.hibernate.validator.constraints.kor.KorRRN;
import org.hibernate.validator.test.constraints.annotations.AbstractConstrainedTest;

import org.testng.annotations.Test;

public class KorRRNValidatorTest extends AbstractConstrainedTest {

@Test
void testKorRRN() {
final KorRegistrationCard korRegistrationCard = new KorRegistrationCard( "030205-3069010" );
final Set<ConstraintViolation<KorRegistrationCard>> violations = validator.validate( korRegistrationCard );
assertNoViolations( violations );
}

@Test
public void testKorRRNInvalid() {
final KorRegistrationCard korRegistrationCard = new KorRegistrationCard( "010101-1063015" );
final Set<ConstraintViolation<KorRegistrationCard>> violations = validator.validate( korRegistrationCard );
assertThat( violations )
.containsOnlyViolations( violationOf( KorRRN.class )
.withMessage( "invalid Korean resident registration number (KorRRN)" ) );
}

private static class KorRegistrationCard {

@KorRRN
private final String rrn;

public KorRegistrationCard(String rrn) {
this.rrn = rrn;
}
}
}
Loading