package com.softwaremill.common.cdi.validation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by amorfis on Dec 3, 2010 2:10:58 PM */ public class PasswordValidator implements ConstraintValidator<Password, String> { private int lowercaseRequired; private int uppercaseRequired; private int numbersRequired; private int specialRequired; private int minLength; private String noLowerCaseMessage; private String noUpperCaseMessage; private Pattern lowercasePattern = Pattern.compile("[a-z]"); private Pattern uppercasePattern = Pattern.compile("[A-Z]"); private Pattern numbersPattern = Pattern.compile("[0-9]"); private Pattern specialPattern = Pattern.compile("[^A-Za-z0-9]"); private String noNumbersMessage; private String noSpecialMessage; private String tooShortMessage; public void initialize(Password constraint) { lowercaseRequired = constraint.lowercaseRequired(); uppercaseRequired = constraint.uppercaseRequired(); numbersRequired = constraint.digitsRequired(); specialRequired = constraint.specialRequired(); minLength = constraint.minLength(); noLowerCaseMessage = constraint.noLowerCaseMessage(); noUpperCaseMessage = constraint.noUpperCaseMessage(); noNumbersMessage = constraint.noDigitsMessage(); noSpecialMessage = constraint.noSpecialMessage(); tooShortMessage = constraint.tooShortMessage(); } public boolean isValid(String pass, ConstraintValidatorContext context) { if (pass == null) return true; ConstraintsMatcher builder = new ConstraintsMatcher(pass, context) .check(lowercasePattern, lowercaseRequired, noLowerCaseMessage) .check(uppercasePattern, uppercaseRequired, noUpperCaseMessage) .check(numbersPattern, numbersRequired, noNumbersMessage) .check(specialPattern, specialRequired, noSpecialMessage) .checkLength(minLength, tooShortMessage); return builder.matches(); } private static class ConstraintsMatcher { private String password; private ConstraintValidatorContext context; private boolean matches = true; public ConstraintsMatcher(String password, ConstraintValidatorContext context) { this.password = password; this.context = context; } public ConstraintsMatcher hasMinLength(int minLength) { if (matches) { matches = password.length() >= minLength; } return this; } public boolean matches() { return matches; } public ConstraintsMatcher check(Pattern pattern, int occurenciesRequired, String errorMessage) { if (matches && occurenciesRequired > 0) { Matcher m = pattern.matcher(password); int count = 0; while (m.find()) { count++; } if (count < occurenciesRequired) { createError(errorMessage); } } return this; } public ConstraintsMatcher checkLength(int minLength, String errorMessage) { if (matches && minLength > 0) { if (password.length() < minLength) { createError(errorMessage); } } return this; } private void createError(String message) { context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); matches = false; } } }