001    // Copyright 2007 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.form.validator;
016    
017    import org.apache.tapestry.IMarkupWriter;
018    import org.apache.tapestry.IRequestCycle;
019    import org.apache.tapestry.form.FormComponentContributorContext;
020    import org.apache.tapestry.form.IFormComponent;
021    import org.apache.tapestry.form.ValidationMessages;
022    import org.apache.tapestry.json.JSONLiteral;
023    import org.apache.tapestry.json.JSONObject;
024    import org.apache.tapestry.valid.ValidationConstants;
025    import org.apache.tapestry.valid.ValidationConstraint;
026    import org.apache.tapestry.valid.ValidationStrings;
027    import org.apache.tapestry.valid.ValidatorException;
028    
029    /**
030     * Validates that the input value is the same as the value of another field.
031     * This validator can also work in 'differ' mode.
032     * <p/>
033     * Apply this validator to the second field in question and define the name
034     * of the component against which to compare the current value.
035     *
036     * @since 4.1.2
037     */
038    public class Identity extends BaseValidator {
039    
040        private String _fieldName;
041        private int _matchType;
042    
043        private static final int DIFFER = 0;
044        private static final int MATCH = 1;
045    
046        public Identity()
047        {
048            super();
049        }
050    
051        public Identity(String initializer)
052        {
053            super(initializer);
054        }
055    
056        public String toString(IFormComponent field, Object value)
057        {
058            if (value == null)
059                return null;
060    
061            return value.toString();
062        }
063    
064        public void validate(IFormComponent field, ValidationMessages messages, Object object)
065                throws ValidatorException
066        {
067            IFormComponent referent = (IFormComponent) field.getContainer().getComponent(_fieldName);
068            Object referentValue = referent.getBinding("value").getObject();
069    
070            //TODO: if component is null treat _fieldName as an ognl expression
071            boolean notEq = notEqual(referentValue, object);
072    
073            if (_matchType == MATCH ? notEq : !notEq)
074                throw new ValidatorException(buildIdentityMessage(messages, field, referent),
075                        ValidationConstraint.CONSISTENCY);
076        }
077    
078        public void renderContribution(IMarkupWriter writer, IRequestCycle cycle,
079                                       FormComponentContributorContext context, IFormComponent field)
080        {
081            if (field.isDisabled())
082                return;
083    
084            IFormComponent referent = (IFormComponent) field.getContainer().getComponent(_fieldName);
085    
086            JSONObject profile = context.getProfile();
087    
088            if (!profile.has(ValidationConstants.CONSTRAINTS)) {
089                profile.put(ValidationConstants.CONSTRAINTS, new JSONObject());
090            }
091            JSONObject cons = profile.getJSONObject(ValidationConstants.CONSTRAINTS);
092    
093            String func = (_matchType == MATCH) ?
094                    "tapestry.form.validation.isEqual" :
095                    "tapestry.form.validation.isNotEqual";
096    
097            accumulateProperty(cons, field.getClientId(),
098                    new JSONLiteral("[" + func + ",\""
099                                    + referent.getClientId() + "\"]"));
100            // could define and use a new ValidationConstants.CONFIRM here to apply to
101            // the profile, but it doesn't support differ.
102            accumulateProfileProperty(field, profile,
103                    ValidationConstants.CONSTRAINTS, buildIdentityMessage(context, field, referent));
104        }
105    
106        public String getMatch()
107        {
108            return _fieldName;
109        }
110    
111        public void setMatch(String field)
112        {
113            _fieldName = field;
114            _matchType = MATCH;
115    
116        }
117    
118        public String getDiffer()
119        {
120            return _fieldName;
121        }
122    
123        public void setDiffer(String field)
124        {
125            _fieldName = field;
126            _matchType = DIFFER;
127        }
128        
129        protected String buildIdentityMessage(ValidationMessages messages, IFormComponent field, IFormComponent referent)
130        {
131            Object[] parameters = new Object[]{
132                    field.getDisplayName(), new Integer(_matchType), referent.getDisplayName()
133            };
134            
135            return messages.formatValidationMessage(getMessage(),
136                    ValidationStrings.INVALID_FIELD_EQUALITY, parameters);
137        }
138    
139        private boolean notEqual(Object o1, Object o2)
140        {
141            if (o1 == null && o2 == null)
142                return false;
143            if (o1 == null || o2 == null)
144                return true;
145            
146            return !o1.equals(o2);
147        }
148    
149    }