TDT4100/src/main/java/encapsulation/Person.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;
}
}