Fork us on GitHub

Validation, RegEx & Masking

Up until recently we had to handcode validation logic into ev...
Post Image

Validation, RegEx & Masking

Up until recently we had to handcode validation logic into every form, this becomes tedious as we work thru larger applications. Some developers built their own generic logic, which leads to obvious duplication of effort. In the interest of keeping everything together we decided to release a standardized validation framework that allows us to define constraints on a set of components, mark invalid entries and disable submission buttons.

Since we wanted regular expressions as a feature in the validation framework we incorporated an old regular expression cn1lib that Steve ported into the API as well. This allows us to write regex in Codename One although we didn't integrate it as deeply as it is in JavaSE to avoid some of the overhead and potential incompatibilities. You can see the RegEx package description for further details.

Using validation is pretty trivial, we use the Validator class to add constraints which define the validation constraints for a specific component. We can also define the components to disable/enable based on validation state and the way in which validation errors are rendered (change the components UIID, paint an emblem on top etc.). A Constraint is an interface that represents validation requirements. You can define a constraint in Java or use some of the builtin constraints such as LengthConstraint, RegexConstraint etc.

Masking isn't a part of validation per se but it is such a common concept that I feel its use is warranted in this short tutorial. Masking allows us to define input with a given structure e.g. credit card comprising of 16 digits split into groups of 4. This is common for date entry and many other types of input, we'd love to offer a more powerful masked input API in the future but for now you can implement a poor mans solution by using a DataChangedListener and stopping the current editing/moving to the next field based on constraints. We implemented such masking as a sample in the code below together with a rather complete validation sample.

This sample below has a couple of neat tricks, the UI changes between tablet and phone form factor. It demonstrates masking and various types of validation. It also demonstrates disabling the submit button to show that validation failed and prevent the user from proceeding.

public void start() {
    if(current != null){
        current.show();
        return;
    }
    Form val = new Form("Validation");
    TableLayout tl;
    int spanButton = 2;
    if(Display.getInstance().isTablet()) {
        tl = new TableLayout(7, 2);
    } else {
        tl = new TableLayout(14, 1);
        spanButton = 1;
    }
    tl.setGrowHorizontally(true);
    val.setLayout(tl);

    val.addComponent(new Label("First Name"));
    TextField firstName = new TextField();
    val.addComponent(firstName);

    val.addComponent(new Label("Surname"));
    TextField surname = new TextField();
    val.addComponent(surname);

    val.addComponent(new Label("E-Mail"));
    TextField email = new TextField();
    email.setConstraint(TextArea.EMAILADDR);
    val.addComponent(email);

    val.addComponent(new Label("URL"));
    TextField url = new TextField();
    url.setConstraint(TextArea.URL);
    val.addComponent(url);

    val.addComponent(new Label("Phone"));
    TextField phone = new TextField();
    phone.setConstraint(TextArea.PHONENUMBER);
    val.addComponent(phone);

    val.addComponent(new Label("Credit Card"));

    Container creditCardContainer = new Container(new GridLayout(1, 4));
    final TextField num1 = new TextField(4);
    final TextField num2 = new TextField(4);
    final TextField num3 = new TextField(4);
    final TextField num4 = new TextField(4);
    num1.setConstraint(TextArea.NUMERIC);
    num2.setConstraint(TextArea.NUMERIC);
    num3.setConstraint(TextArea.NUMERIC);
    num4.setConstraint(TextArea.NUMERIC);
    creditCardContainer.addComponent(num1);
    creditCardContainer.addComponent(num2);
    creditCardContainer.addComponent(num3);
    creditCardContainer.addComponent(num4);
    val.addComponent(creditCardContainer);

    Button submit = new Button("Submit");
    TableLayout.Constraint cn = tl.createConstraint();
    cn.setHorizontalSpan(spanButton);
    cn.setHorizontalAlign(Component.RIGHT);
    val.addComponent(cn, submit);

    Validator v = new Validator();
    v.addConstraint(firstName, new LengthConstraint(2)).
            addConstraint(surname, new LengthConstraint(2)).
            addConstraint(url, RegexConstraint.validURL()).
            addConstraint(email, RegexConstraint.validEmail()).
            addConstraint(phone, new RegexConstraint(phoneRegex, "Must be valid phone number")).
            addConstraint(num1, new LengthConstraint(4)).
            addConstraint(num2, new LengthConstraint(4)).
            addConstraint(num3, new LengthConstraint(4)).
            addConstraint(num4, new LengthConstraint(4));

    automoveToNext(num1, num2);
    automoveToNext(num2, num3);
    automoveToNext(num3, num4);

    v.addSubmitButtons(submit);

    val.show();
}

private void automoveToNext(final TextField current, final TextField next) {
    current.addDataChangeListener(new DataChangedListener() {
        public void dataChanged(int type, int index) {
            if(current.getText().length() == 5) {
                Display.getInstance().stopEditing(current);
                String val = current.getText();
                current.setText(val.substring(0, 4));
                next.setText(val.substring(4));
                Display.getInstance().editString(next, 5, current.getConstraint(), next.getText());
            }
        }
    });        
}
Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.