/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.formio.validation.constraints.cz;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Validation of Czech "Rodne cislo".
*
* @author Radek Beran
*/
public final class RodneCisloValidation {
private static final Pattern RC_PATTERN = Pattern.compile("^(\\d\\d)(\\d\\d)(\\d\\d)[/]?(\\d\\d\\d)(\\d?)$");
/**
* Returns true if given "rodne cislo" is valid according to complex rules
* (not only the format is validated).
* <p>
* RC can but need not to contains slash.
*
* @param rodneCislo
* @return result of validation, false if rodneCislo is {@code null}
*/
public static boolean isRodneCislo(String rodneCislo) {
if (rodneCislo == null || rodneCislo.isEmpty()) return false;
// Validni RC je napr. 780123/3540, 0531135099, 0681186066
Matcher matcher = RC_PATTERN.matcher(rodneCislo);
boolean valid = matcher.matches();
if (valid) {
// Input string answers to regular expression pattern
matcher.reset();
String yearStr = null, monthStr = null, dayStr = null, extStr = null, cStr = "";
int year = 0, month = 0, c = 0;
boolean cParsed = false;
// Extracting parts within parenthesis
while (matcher.find()) {
yearStr = matcher.group(1);
monthStr = matcher.group(2);
dayStr = matcher.group(3);
extStr = matcher.group(4);
year = Integer.parseInt(yearStr);
month = Integer.parseInt(monthStr);
if (matcher.groupCount() > 4 && !matcher.group(5).trim().isEmpty()) {
cStr = matcher.group(5);
c = Integer.parseInt(cStr);
cParsed = true;
}
}
// Do roku 1954 pridelovana devitimistna RC nelze overit.
// Take RC, ktera v pripade pristehovanych cizincu konci na 9999
// nelze validovat a zvaliduje se jen datum.
if (!cParsed || (extStr + cStr).equals("9999")) {
if (!cParsed) {
valid = year < 54;
}
} else {
// posledni kontrolni cislice
// vypocitame zbytek po deleni prvnich deviti cislic a cisla 11
Integer firstNineDigits = Integer.valueOf(yearStr + monthStr + dayStr + extStr);
int mod = firstNineDigits.intValue() % 11;
// je-li zbytek 10, musi byt posledni cislice 0, jinak posledni cislice musi byt rovna zbytku
if (mod == 10) {
mod = 0;
}
valid = (c == mod);
}
if (valid) {
// kontrola data
year += year < 54 ? 2000 : 1900;
yearStr = year + "";
// k mesici muze byt pripocteno 20, 50 nebo 70
if (month > 70 && year > 2003) {
month -= 70;
} else if (month > 50) {
month -= 50;
} else if (month > 20 && year > 2003) {
month -= 20;
}
monthStr = month + "";
if (monthStr.length() == 1) {
monthStr = "0" + monthStr;
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
format.setLenient(false); // strict parsing
try {
format.parse(yearStr + "-" + monthStr + "-" + dayStr);
} catch (ParseException ex) {
valid = false;
}
}
}
return valid;
}
private RodneCisloValidation() {
throw new AssertionError("Not instantiable, use static members");
}
}