I've converted my PHP validation class to JAVA and this is what I came up with. I'm not exactly sure how user input gets validated in JAVA world yet as I've just started learning the language but I'm sure this class could be useful in some cases.

import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Pattern;

public class Validation {

    private final Map<String, String> input;

    private final Map<String, String> rules;

    private final Map<String, List<String>> errors = new HashMap<>();

    private final Map<String, String> validated = new HashMap<>();

    // Set default messages
    private Map<String, String> strictMessages = new HashMap<String, String>() {{
        put("numeric", ":field must consist of only numbers.");
        put("alpha", ":field must only consist of alphabetical characters.");
        put("lowercase", ":field must have all lowercase letters.");
        put("uppercase", ":field must have all uppercase letters.");
    }};

    private Map<String, String> messages = new HashMap<String, String>() {{
        put("required", ":field is required.");
        put("email", "The :field must be a valid email address.");
        put("phone", ":field is not a valid phone number.");
        put("unique", ":field already exists in our records.");
        put("confirm", ":field does not match with the :confirm field.");
        put("numeric", ":field must have numbers.");
        put("alpha", ":field must have a letter.");
        put("lowercase", ":field must have a lowercase letter");
        put("uppercase", ":field must have an uppercase letter.");
        put("mixed", ":field must contain a mix of numbers and letters.");
        put("mixedCase", ":field must consist of uppercase and lowercase letters.");
        put("minLength", ":field must be at least :min characters long.");
        put("maxLength", ":field must not exceed :max characters in length.");
        put("length", ":field must be between :min and :max characters in length.");
        put("min", ":field must be at least :min or higher.");
        put("max", ":field must not exceed :max.");
        put("range", ":field must be between :min and :max.");
        put("special", ":field must contain special characters.");
    }};

    private final String[] placeholders = {":min", ":max", ":confirm"};

    private final Map<String, Pattern> patterns = new HashMap<String, Pattern>() {{
        put("phone", Pattern.compile("^\\(\\d{3}\\) \\d{3}-\\d{4}$"));
        put("uppercase", Pattern.compile(".*[A-Z].*"));
        put("uppercaseStrict", Pattern.compile("^[A-Z]+$"));
        put("lowercase", Pattern.compile(".*[a-z].*"));
        put("lowercaseStrict", Pattern.compile("^[a-z]+$"));
        put("alpha", Pattern.compile(".*[a-zA-Z].*"));
        put("alphaStrict", Pattern.compile("[a-zA-Z]+"));
        put("numeric", Pattern.compile(".*\\d.*"));
        put("numericStrict", Pattern.compile("\\d+"));
        put("email", Pattern.compile("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,6}$"));
        put("seperators", Pattern.compile("[_-]"));
        put("specialChars", Pattern.compile(".*[$&+,:;=\\\\?@#|/'<>.^*()%!-].*"));
    }};

    private Method currentMethod;

    private boolean strict = false;

    public Validation(Map<String, String> input, Map<String, String> rules) {
        this.input = input;
        this.rules = rules;
    }

    public Validation(Map<String, String> input, Map<String, String> rules, Map<String, String> customMessages) {
        this(input, rules);
        this.messages = customMessages;
    }

    public Validation(Map<String, String> input, Map<String, String> rules, Map<String, String> customMessages, Map<String, String> customStrictMessages) {
        this(input, rules, customMessages);
        this.strictMessages = customStrictMessages;
    }

    public boolean validate() throws InvocationTargetException, IllegalAccessException {
        for(Map.Entry<String, String> ruleEntry : this.rules.entrySet()) {
            String[] rules = ruleEntry.getValue().split("\\|");

            for(String rule : rules) {
                applyRule(ruleEntry.getKey(), rule);
            }
        }

        return errors.isEmpty();
    }

    private void applyRule(String field, String rule) throws InvocationTargetException, IllegalAccessException {
        String[] params = new String[0];

        if (rule.contains(":")) {
            String[] rulesAndParams = rule.split(":", 2);
            rule = rulesAndParams[0];
            params = rulesAndParams[1].split(",");
        }

        String ruleMethod = "validate"+ rule.substring(0, 1).toUpperCase() + rule.substring(1);

        // Resetting strict to false each iteration
        strict = false;

        if(ruleMethodExists(ruleMethod)) {
            boolean result = validateField(field, params);
            handleResult(result, field, rule, params);
        }
    }

    private boolean ruleMethodExists(String possibleRuleMethod) {
        Method[] ruleMethods = getClass().getDeclaredMethods();

        for(Method ruleMethod : ruleMethods) {
            if(ruleMethod.getName().equals(possibleRuleMethod)) {
                currentMethod = ruleMethod;
                return true;
            }
        }

        // Rule doesn't exist (Should never happen as they are hard-coded)
        return false;
    }

    private boolean validateField(String field, String[] params) throws InvocationTargetException, IllegalAccessException {
        Object[] methodSignature = new Object[]{field, params};

        if(input.containsKey(field)) {
            return (boolean) currentMethod.invoke(this, methodSignature);
        }

        // The input field does not exist (Should never happen)
        return false;
    }

    private void handleResult(boolean result, String field, String rule, String[] params) {
        if(!result && (!rules.get(field).contains("nullable") || !input.get(field).isEmpty())) {
            String errorMessage = getErrorMessage(field, rule, params);
            errors.computeIfAbsent(field, k -> new ArrayList<>()).add(errorMessage);
        }
        else {
            // Not adding any repeating fields that are used to confirm another field
            if(!rules.get(field).contains("confirm")) {
                validated.put(field, input.get(field));
            }
        }
    }

    private boolean validateNullable(String field, String[] params) {
        return true;
    }

    private boolean validateRequired(String field, String[] params) {
        return !input.get(field).trim().isEmpty();
    }

    private boolean validateMinLength(String field, String[] params) {
        return input.get(field).length() >= Integer.parseInt(params[0]);
    }

    private boolean validateMaxLength(String field, String[] params) {
        return input.get(field).length() <= Integer.parseInt(params[0]);
    }

    private boolean validateLength(String field, String[] params) {
        int length = input.get(field).length();
        return length >= Integer.parseInt(params[0]) && length <= Integer.parseInt(params[1]);
    }

    private boolean validateMin(String field, String[] params) {
        return Long.parseLong(input.get(field)) >= Long.parseLong(params[0]);
    }

    private boolean validateMax(String field, String[] params) {
        return Long.parseLong(input.get(field)) <= Long.parseLong(params[0]);
    }

    private boolean validateRange(String field, String[] params) {
        long value = Long.parseLong(input.get(field));
        return value >= Long.parseLong(params[0]) && value <= Long.parseLong(params[1]);
    }

    private boolean validateEmail(String field, String[] params) {
        return getPattern("email").matcher(input.get(field)).matches();
    }

    private boolean validatePhone(String field, String[] params) {
        return getPattern("phone").matcher(input.get(field)).matches();
    }

    private boolean validateConfirm(String field, String[] params) {
        return input.get(field).equals(input.get(params[0]));
    }

    private boolean validateAlpha(String field, String[] params) {
        return setMode(params).setPattern("alpha").matcher(input.get(field)).matches();
    }

    private boolean validateNumeric(String field, String[] params) {
        return setMode(params).setPattern("numeric").matcher(input.get(field)).matches();
    }

    private boolean validateMixed(String field, String[] params) {
        return validateAlpha(field, params) && validateNumeric(field, params);
    }

    private boolean validateLowercase(String field, String[] params) {
        return setMode(params).setPattern("lowercase").matcher(input.get(field)).matches();
    }

    private boolean validateUppercase(String field, String[] params) {
        return setMode(params).setPattern("uppercase").matcher(input.get(field)).matches();
    }

    private boolean validateMixedCase(String field, String[] params) {
        return validateLowercase(field, params) && validateUppercase(field, params);
    }

    private boolean validateSpecial(String field, String[] params) {
        // No strict mode. Why have a field that requires all special characters?
        return getPattern("specialChars").matcher(input.get(field)).matches();
    }

    private Validation setMode(String[] params) {
        if(params != null && params.length > 0 && !params[0].isEmpty()) {
            if(params[0].equals("strict")) {
                // To ensure we get the strict version of the error message
                strict = true;
            }
        }

        return this;
    }

    private Pattern setPattern(String rule) {
        return getPattern(rule + ((strict) ? "Strict" : ""));
    }

    private String getErrorMessage(String field, String rule, String[] params) {
        String message = prepareErrorMessage(rule);

        for(String param : params) {
            for(String placeholder : placeholders) {
                if(message.contains(placeholder)) {
                    message = message.replace(placeholder, param);
                    break;
                }
            }
        }

        return message.replace(":field", toTitleCase(field));
    }

    // Used to change a field from either (field_name or field-name) to Field Name
    private String toTitleCase(String field) {
        String[] fieldParts = getPattern("seperators").split(field);
        StringBuilder parsedField = new StringBuilder();

        for(String part : fieldParts) {
            // Making the first character of each word uppercase
            // and adding a space to the end to space out the words.
            parsedField.append(part.substring(0, 1).toUpperCase()).append(part.substring(1)).append(" ");
        }

        // Trimming any extra spaces before returning the parsed field
        return parsedField.toString().trim();
    }

    private String prepareErrorMessage(String rule) {
        String defaultMessage = "Validation failed for rule "+ rule;

        return (strict ? strictMessages : messages).getOrDefault(rule, defaultMessage);
    }

    // Getter methods for accessing the data
    public Map getValidated() {
        return validated;
    }

    public Map getErrors() {
        return errors;
    }

    public Pattern getPattern(String key) {
        return patterns.get(key);
    }
}

This code snippet was published on It was last edited on

1

1 Comment

  • Votes
  • Oldest
  • Latest
BO
443 9
Commented
Updated
import java.lang.reflect.InvocationTargetException;
import java.util.*;

class Main {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {

        Map<String, String> input = new HashMap<String, String>() {{
            put("username", "some Name");
            put("email", "sample@email.com");
            put("password", "12345678dF#");
            put("password_confirm", "12345678dF#");
            put("phone", "(123) 456-7890");
            put("phoneAlt", "1234567890");
            put("word", "alphabet");
            put("anotherword", "FGHJ");
            put("a_number", "12324");
            put("another_number", "175");
        }};

        Map<String, String> rules = new HashMap<String, String>() {{
            put("username", "required|minLength:4|alpha|uppercase");
            put("email", "required|email");
            put("password", "required|mixed|mixedCase|length:4,15|special");
            put("password_confirm", "required|confirm:password|mixed");
            put("phone", "nullable|phone");
            put("phoneAlt", "minLength:10|maxLength:10|numeric:strict");
            put("word", "alpha:strict|lowercase:strict");
            put("anotherword", "uppercase:strict");
            put("a_number", "numeric:strict|min:12321|max:12325");
            put("another_number", "numeric:strict|range:100,200");
        }};

        Validation validate = new Validation(input, rules);

        if(!validate.validate()) {
            System.out.println("Validation failed\n");
            Map<String, List<String>> errors = validate.getErrors();

            for (Map.Entry<String, List<String>> entry : errors.entrySet()) {
                String field = entry.getKey();
                List<String> errorMessages = entry.getValue();

                System.out.println("Field: " + field);

                for (String errorMessage : errorMessages) {
                    System.out.println("\tError: " + errorMessage);
                }

                System.out.println();
            }
        }
        else {
            System.out.println("Validation passed!\n");
        }

        Map<String, String> validatedFields = validate.getValidated();

        for(Map.Entry<String, String> validated : validatedFields.entrySet()) {
            System.out.print(validated.getKey() +" : ");
            System.out.println(validated.getValue());
        }
    }
}

This would be an example usage of the validation class. Just put all the user input and rules into a map and run them against each other in the class. It's a bit different than the PHP class, where the min/max/range is to make sure a numeric value is within the expected range and minLength, maxLength, length is for length size of a string.

add a comment
1