Search code examples
validationjsfprimefacesserver-sideinput-mask

Primefaces inputMask server side validation


When using a component we specify how to validate it with a required, maxlength or a validator and those validation are enforced on the server which is great.

When using Primefaces inputMask we can specify a mask. The mask lead us to think that the user input will always match this mask. Unfortunately this is not the case and we have to code a validator to make sure that the user input will be the way we want it to be. So, unless I am missing something, the "client-side only" behavior of the inputMask is just good enough to provide a hint to the user on how to fill a field.

Is there a generic validator than can be paired with p:inputMask to make sure that the user input is really matching the mask?


Solution

  • PrimeFaces doesn't offer that out the box.

    It should however be relatively trivial to convert a mask pattern to a regex pattern using a simple for loop over a character array. You can then use this regex pattern to validate the value.

    E.g.

    <p:inputMask ... validator="maskValidator">
    

    with

    @FacesValidator("maskValidator")
    public class MaskValidator implements Validator {
    
        @Override
        public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
            String submittedValue = context.getExternalContext().getRequestParameterMap().get(component.getClientId(context));
    
            if (submittedValue == null || submittedValue.isEmpty()) {
                return; // Let @NotNull / required="true" handle.
            }
    
            InputMask input = (InputMask) component;
            String mask = input.getMask();
            StringBuilder regex = new StringBuilder();
    
            for (char c : mask.toCharArray()) {
                switch (c) {
                    case 'a': regex.append("[a-zA-Z]"); break;
                    case '9': regex.append("[0-9]"); break;
                    case '?': case '+': case '*': regex.append(c); break;
                    default: regex.append(Pattern.quote(Character.toString(c))); break;
                }
            }
    
            if (!submittedValue.matches(regex.toString())) {
                throw new ValidatorException(new FacesMessage(submittedValue + " does not match " + mask));
            }
        }
    }
    

    Note that the validator works with unconverted submitted value, not with the passed-in 3rd argument, which might be (implicitly) converted beforehand and thus have a potentially different toString() representation.