231 lines
7.8 KiB
Java
231 lines
7.8 KiB
Java
package encapsulation;
|
|
|
|
import java.text.DateFormat;
|
|
import java.text.ParseException;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.function.BiFunction;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.IntStream;
|
|
import java.util.stream.Stream;
|
|
|
|
class Person {
|
|
private String[] name = {""};
|
|
private String email = "";
|
|
private Date birthday;
|
|
private char gender = '\0';
|
|
private String ssn;
|
|
|
|
public Date getBirthday() {
|
|
return this.birthday;
|
|
}
|
|
|
|
public String getEmail() {
|
|
return this.email;
|
|
}
|
|
|
|
public char getGender() {
|
|
return this.gender;
|
|
}
|
|
|
|
public String getName() {
|
|
return String.join(" ", this.name);
|
|
}
|
|
|
|
public String getSSN() {
|
|
return this.ssn;
|
|
}
|
|
|
|
public void setName(String name) throws IllegalArgumentException {
|
|
final String nameFormat = "[A-ZÆØÅa-zæøå]{2,}";
|
|
final String fullNameFormat = String.format("%s %s", nameFormat, nameFormat);
|
|
|
|
if (!name.matches(fullNameFormat))
|
|
throw new IllegalArgumentException("Couldn't parse name");
|
|
|
|
this.name = name.split(" ");
|
|
}
|
|
|
|
private boolean checkEmail(String email) {
|
|
final Set<String> cTLDs = Set.of("ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw",
|
|
"ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs",
|
|
"bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr",
|
|
"cu", "cv", "cw", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es",
|
|
"et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn",
|
|
"gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im",
|
|
"in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr",
|
|
"kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me",
|
|
"mf", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my",
|
|
"mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg",
|
|
"ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb",
|
|
"sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy",
|
|
"sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua",
|
|
"ug", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "ye", "yt", "za", "zm",
|
|
"zw");
|
|
|
|
String[] emailParts = email.split("[.@]");
|
|
|
|
if (emailParts.length != 4) return false;
|
|
return Stream.of(
|
|
emailParts[0].equalsIgnoreCase(this.name[0]),
|
|
emailParts[1].equalsIgnoreCase(this.name[1]),
|
|
emailParts[2].matches("[A-Za-z0-9]+"), // "\w" includes '_', so it's no good here.
|
|
cTLDs.contains(emailParts[3])
|
|
).allMatch(b -> b);
|
|
}
|
|
|
|
public void setEmail(String email) throws IllegalArgumentException {
|
|
if (!checkEmail(email))
|
|
throw new IllegalArgumentException("Couldn't parse email. Is the name correct?");
|
|
|
|
this.email = email;
|
|
}
|
|
|
|
public void setBirthday(Date birthday) throws IllegalArgumentException {
|
|
Date now = new Date(System.currentTimeMillis());
|
|
if (!birthday.before(now))
|
|
throw new IllegalArgumentException("This date is invalid. Please set a date in the past.");
|
|
|
|
this.birthday = birthday;
|
|
}
|
|
|
|
public void setGender(char gender) throws IllegalArgumentException {
|
|
final Set<Character> legalGenders = Set.of('M', 'F', '\0');
|
|
|
|
if (!legalGenders.contains(gender))
|
|
throw new IllegalArgumentException("Couldn't parse gender.");
|
|
|
|
this.gender = gender;
|
|
}
|
|
|
|
/**
|
|
* Emphasize a range of characters within a String
|
|
* @param s The string to modify
|
|
* @param start The start index of the range
|
|
* @param end The end index of the range
|
|
* @return The modified string with an emphazised range
|
|
*/
|
|
private static String emphasizeStringRange(String s, int start, int end)
|
|
throws IndexOutOfBoundsException {
|
|
return s.substring(0, start)
|
|
+ " <"
|
|
+ s.substring(start, end)
|
|
+ "> "
|
|
+ s.substring(end);
|
|
}
|
|
|
|
/**
|
|
* Make sure that the birthdate of the SSN is equal to the
|
|
* birthday of the person
|
|
* @param ssn The SSN to validate
|
|
*/
|
|
private void checkSSNBirthday(String ssn) throws IllegalArgumentException {
|
|
|
|
// Calendar birthdate = Calendar.getInstance();
|
|
|
|
try {
|
|
Date birthday =
|
|
DateFormat
|
|
.getDateInstance(DateFormat.SHORT)
|
|
.parse(
|
|
ssn.substring(0, 2) + "/" +
|
|
ssn.substring(2, 4) + "/" +
|
|
ssn.substring(4, 6)
|
|
);
|
|
|
|
if (!birthday.equals(this.birthday))
|
|
throw new IllegalArgumentException(
|
|
"The SSN birthday does not match this persons birthday"
|
|
);
|
|
|
|
} catch (ParseException e) {
|
|
throw new IllegalArgumentException(
|
|
"Could not parse SSN date: " + emphasizeStringRange(ssn, 0, 6)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make sure that the SSN is valid based on the persons gender
|
|
* @param ssn The ssn to validate
|
|
*/
|
|
private void checkSSNMaleFemaleDigit(String ssn) throws IllegalArgumentException {
|
|
int maleOrFemaleDigit = Integer.parseInt(ssn.substring(8, 9));
|
|
boolean isMale = maleOrFemaleDigit % 2 == 1;
|
|
boolean isGenderCorrect =
|
|
(this.gender == 'M' && isMale) || (this.gender == 'F' && !isMale);
|
|
|
|
if (!isGenderCorrect)
|
|
throw new IllegalArgumentException(
|
|
"The digit "
|
|
+ emphasizeStringRange(ssn, 8, 9)
|
|
+ " has to be different because the person is a "
|
|
+ (isMale ? "male" : "female")
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check that the control ciphers are valid.
|
|
* @param ssn The SSN to validate
|
|
*/
|
|
private void checkSSNControlCiphers(String ssn) throws IllegalArgumentException {
|
|
|
|
final List<Integer> f = List.of(3, 7, 6, 1, 8, 9, 4, 5, 2);
|
|
final List<Integer> g = List.of(5, 4, 3, 2, 7, 6, 5, 4, 3, 2);
|
|
|
|
final BiFunction<List<Integer>, List<Integer>, Integer> processDigits =
|
|
(ds, cs) ->
|
|
IntStream
|
|
.range(0, ds.size())
|
|
.map(i -> ds.get(i) * cs.get(i))
|
|
.sum();
|
|
|
|
List<Integer> digits =
|
|
ssn
|
|
.substring(0, 9)
|
|
.chars()
|
|
.map(c -> c - '0') // remove offset from 0x00 to '0'
|
|
.boxed() // IntStream -> Stream<Integer>
|
|
.collect(Collectors.toList());
|
|
|
|
Integer k1 = 11 - processDigits.apply(digits, f) % 11;
|
|
digits.add(k1);
|
|
Integer k2 = 11 - processDigits.apply(digits, g) % 11;
|
|
|
|
boolean isControlCiphersCorrect =
|
|
(k1.toString() + k2.toString())
|
|
.equals(ssn.substring(9));
|
|
|
|
if (!isControlCiphersCorrect)
|
|
throw new IllegalArgumentException(
|
|
String.format(
|
|
"""
|
|
Control ciphers do not match the given input: %s
|
|
Expected: %s""",
|
|
emphasizeStringRange(ssn, 9, 11),
|
|
(k1 + " " + k2)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Validate an SSN based on the persons info.
|
|
* {@link #setBirthday(Date) setBirthday()} and {@link #setGender(char) setGender()}
|
|
* must have been run in order for this function to work.
|
|
* @param ssn The SSN to validate
|
|
*/
|
|
private void checkSSN(String ssn) throws IllegalArgumentException {
|
|
if (!ssn.matches("\\d{11}"))
|
|
throw new IllegalArgumentException("The amount of digits does not match the expected amount");
|
|
checkSSNBirthday(ssn);
|
|
checkSSNMaleFemaleDigit(ssn);
|
|
checkSSNControlCiphers(ssn);
|
|
}
|
|
|
|
public void setSSN(String ssn) throws IllegalArgumentException {
|
|
checkSSN(ssn);
|
|
this.ssn = ssn;
|
|
}
|
|
}
|