Collect All Validation Errors
Problem
Every input line is: name age email (a missing name is written as -). Check three rules: the name must not be - (error name: empty); the age must be a whole number from 0 to 150 without a sign (error age: invalid); the email must have exactly one @, at least one character before it, and a dot somewhere after it that is not the last character (error email: invalid). Do not stop at the first problem: collect all problems of the line and print them joined by a semicolon and a space in the order name, age, email. Print ok when there are no problems.
Constraints
- 1 <= lines <= 10^4
Examples
Input
amit 30 amit@mail.com - 200 nomail
Output
ok name: empty; age: invalid; email: invalid
Solutions
Failing at the first problem makes users fix one error, submit again, and find the next. Collecting all problems and reporting them together is friendlier for forms and for file imports. The simple email check here is only a model: real email checking is done by sending a confirmation message.
/*
* Problem : Process lines.
* Input : Several lines.
* Output : ok or the list of problems.
* Approach: Aggregate all errors.
* Time : O(n)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: A ValidationException carries a list of all problems, not only the first one.
static class ValidationException extends RuntimeException {
final java.util.List<String> problems;
ValidationException(java.util.List<String> problems) {
super(String.join("; ", problems));
this.problems = problems;
}
}
// Step 2: Check every rule, remember each problem, and throw once at the end if the list is not empty.
static void validate(String name, String age, String email) {
java.util.List<String> problems = new java.util.ArrayList<>();
if (name.equals("-")) {
problems.add("name: empty");
}
boolean ageOk = age.matches("[0-9]+") && age.length() <= 3 && Integer.parseInt(age) <= 150;
if (!ageOk) {
problems.add("age: invalid");
}
int at = email.indexOf('@');
boolean emailOk = at > 0 && at == email.lastIndexOf('@')
&& email.indexOf('.', at + 1) >= 0 && !email.endsWith(".");
if (!emailOk) {
problems.add("email: invalid");
}
if (!problems.isEmpty()) {
throw new ValidationException(problems);
}
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(String[] lines) {
String[] output = new String[lines.length];
for (int i = 0; i < lines.length; i++) {
String[] part = lines[i].split(" ");
try {
validate(part[0], part[1], part[2]);
output[i] = "ok";
} catch (ValidationException error) {
// Step 3: The caller gets all problems at once.
output[i] = error.getMessage();
}
}
return output;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
java.util.List<String> all = new java.util.ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
all.add(line);
}
}
String[] lines = all.toArray(new String[0]);
System.out.println(String.join("\n", solve(lines)));
}
}
Hints (1)
- Why is reporting all errors at once friendlier?