Collect All Validation Errors

medium Exception Handling

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

Best approachTime O(n)Space O(n)

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.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * 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)
  1. Why is reporting all errors at once friendlier?