001    /*
002     * Copyright 2002-2006 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.samples.simple.domain;
017    
018    import java.util.Date;
019    
020    import org.springframework.rules.constraint.Constraint;
021    import org.springframework.rules.Rules;
022    import org.springframework.rules.support.DefaultRulesSource;
023    
024    /**
025     * This class is a source for validation rules associated with the domain objects in this application. This clas is
026     * wired into application via the application context configuration like this:
027     * 
028     * <pre>
029     *    &lt;bean id=&quot;rulesSource&quot; 
030     *        class=&quot;org.springframework.richclient.samples.simple.domain.SimpleValidationRulesSource&quot;/&gt;
031     * </pre>
032     * 
033     * With this configuration, validating forms will interrogate the rules source for rules that apply to the class of a
034     * form object (in this case, that's objects of type {@link Contact}.
035     * @author Larry Streepy
036     */
037    public class SimpleValidationRulesSource extends DefaultRulesSource {
038    
039            /**
040             * Basic name validator. Note that the "alphabeticConstraint" argument is a message key used to locate the message
041             * to display when this validator fails.
042             */
043            private final Constraint NAME_CONSTRAINT = all(new Constraint[] { required(), minLength(2),
044                            regexp("[-'.a-zA-Z ]*", "alphabeticConstraint") });
045    
046            /** Zipcode validator, allows NNNNN or NNNNN-NNNN */
047            private final Constraint ZIPCODE_CONSTRAINT = all(new Constraint[] { required(), minLength(5), maxLength(10),
048                            regexp("[0-9]{5}(-[0-9]{4})?", "zipcodeConstraint") });
049    
050            /** Email validator, simply tests for x@y, wrap in ()? so it is optional */
051            private final Constraint EMAIL_CONSTRAINT = all(new Constraint[] { regexp("([-a-zA-Z0-9.]+@[-a-zA-Z0-9.]+)?",
052                            "emailConstraint") });
053    
054            /** Phone number validator, must be 123-456-7890, wrap in ()? so it is optional */
055            private final Constraint PHONE_CONSTRAINT = all(new Constraint[] { regexp("([0-9]{3}-[0-9]{3}-[0-9]{4})?",
056                            "phoneConstraint") });
057    
058            /**
059             * Construct the rules source. Just add all the rules for each class that will be validated.
060             */
061            public SimpleValidationRulesSource() {
062                    super();
063    
064                    // Add the rules specific to the object types we manage
065                    addRules(createContactRules());
066            }
067    
068            /**
069             * Construct the rules that are used to validate a Contact domain object.
070             * @return validation rules
071             * @see Rules
072             */
073            private Rules createContactRules() {
074                    // Construct a Rules object that contains all the constraints we need to apply
075                    // to our domain object. The Rules class offers a lot of convenience methods
076                    // for creating constraints on named properties.
077    
078                    return new Rules(Contact.class) {
079                            protected void initRules() {
080                                    add("firstName", NAME_CONSTRAINT);
081                                    add("lastName", NAME_CONSTRAINT);
082                                    add(not(eqProperty("firstName", "lastName")));
083    
084                                    // If a DOB is specified, it must be in the past
085                                    add("dateOfBirth", lt(new Date()));
086    
087                                    add("emailAddress", EMAIL_CONSTRAINT);
088                                    add("homePhone", PHONE_CONSTRAINT);
089                                    add("workPhone", PHONE_CONSTRAINT);
090    
091                                    add("contactType", required());
092    
093                                    // Note that you can define constraints on nested properties.
094                                    // This is useful when the nested object is not displayed in
095                                    // a form of its own.
096                                    add("address.address1", required());
097                                    add("address.city", required());
098                                    add("address.state", required());
099                                    add("address.zip", ZIPCODE_CONSTRAINT);
100    
101                    add("memo", required());
102                            }
103                    };
104            }
105    }