001    // Copyright 2005 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.hivemind.ApplicationRuntimeException;
018    import org.apache.hivemind.HiveMind;
019    import org.apache.hivemind.util.Defense;
020    import org.apache.hivemind.util.PropertyUtils;
021    import org.apache.tapestry.IComponent;
022    import org.apache.tapestry.util.RegexpMatch;
023    import org.apache.tapestry.util.RegexpMatcher;
024    
025    import java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.List;
028    import java.util.Map;
029    
030    /**
031     * Implementation of the tapestry.form.validator.ValidatorFactory service, which builds and caches
032     * validators and lists of validators from a "magic" string specification.
033     * 
034     * @author Howard Lewis Ship
035     * @since 4.0
036     */
037    public class ValidatorFactoryImpl implements ValidatorFactory
038    {
039        private static final String PATTERN = "^\\s*(\\$?\\w+)\\s*(=\\s*(((?!,|\\[).)*))?";
040    
041        /**
042         * Injected map of validator names to ValidatorContribution.
043         */
044    
045        private Map _validators;
046    
047        public List constructValidatorList(IComponent component, String specification)
048        {
049            Defense.notNull(component, "component");
050    
051            if (HiveMind.isBlank(specification))
052                return Collections.EMPTY_LIST;
053    
054            List result = new ArrayList();
055            String chopped = specification;
056    
057            RegexpMatcher matcher = new RegexpMatcher();
058    
059            while (true)
060            {
061                if (chopped.length() == 0)
062                    break;
063    
064                if (!result.isEmpty())
065                {
066                    if (chopped.charAt(0) != ',')
067                        throw new ApplicationRuntimeException(ValidatorMessages
068                                .badSpecification(specification));
069    
070                    chopped = chopped.substring(1);
071                }
072    
073                RegexpMatch[] matches = matcher.getMatches(PATTERN, chopped);
074    
075                if (matches.length != 1)
076                    throw new ApplicationRuntimeException(ValidatorMessages.badSpecification(specification));
077    
078                RegexpMatch match = matches[0];
079    
080                String name = match.getGroup(1);
081                String value = match.getGroup(3);
082                String message = null;
083    
084                int length = match.getMatchLength();
085    
086                if (chopped.length() > length)
087                {
088                    char lastChar = chopped.charAt(length);
089                    if (lastChar == ',')
090                        length--;
091                    else if (lastChar == '[')
092                    {
093                        int messageClose = chopped.indexOf(']', length);
094                        message = chopped.substring(length + 1, messageClose);
095                        length = messageClose;
096                    }
097                }
098    
099                Validator validator = buildValidator(component, name, value, message);
100    
101                result.add(validator);
102    
103                if (length >= chopped.length())
104                    break;
105    
106                chopped = chopped.substring(length + 1);
107    
108            }
109    
110            return Collections.unmodifiableList(result);
111        }
112    
113        private Validator buildValidator(IComponent component, String name, String value, String message)
114        {
115            if (name.startsWith("$"))
116                return extractValidatorBean(component, name, value, message);
117    
118            ValidatorContribution vc = (ValidatorContribution) _validators.get(name);
119    
120            if (vc == null)
121                throw new ApplicationRuntimeException(ValidatorMessages.unknownValidator(name));
122    
123            if (value == null && vc.isConfigurable())
124                throw new ApplicationRuntimeException(ValidatorMessages.needsConfiguration("name"));
125    
126            if (value != null && !vc.isConfigurable())
127                throw new ApplicationRuntimeException(ValidatorMessages.notConfigurable(name, value));
128    
129            try
130            {
131                Object result = vc.getValidatorClass().newInstance();
132    
133                if (vc.isConfigurable())
134                    PropertyUtils.smartWrite(result, name, value);
135    
136                if (message != null)
137                    PropertyUtils.write(result, "message", message);
138    
139                return (Validator) result;
140            }
141            catch (Exception ex)
142            {
143                throw new ApplicationRuntimeException(ValidatorMessages.errorInitializingValidator(
144                        name,
145                        vc.getValidatorClass(),
146                        ex), ex);
147            }
148        }
149    
150        private Validator extractValidatorBean(IComponent component, String validatorName,
151                String value, String message)
152        {
153            String beanName = validatorName.substring(1);
154    
155            if (HiveMind.isNonBlank(value) || HiveMind.isNonBlank(message))
156                throw new ApplicationRuntimeException(ValidatorMessages
157                        .noValueOrMessageForBean(beanName));
158    
159            return new BeanValidatorWrapper(component, beanName);
160        }
161    
162        public void setValidators(Map validators)
163        {
164            _validators = validators;
165        }
166    }