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 java.util.ArrayList;
018    import java.util.Collections;
019    import java.util.List;
020    import java.util.Map;
021    
022    import org.apache.hivemind.ApplicationRuntimeException;
023    import org.apache.hivemind.HiveMind;
024    import org.apache.hivemind.util.Defense;
025    import org.apache.hivemind.util.PropertyUtils;
026    import org.apache.tapestry.IComponent;
027    import org.apache.tapestry.util.RegexpMatch;
028    import org.apache.tapestry.util.RegexpMatcher;
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
077                            .badSpecification(specification));
078    
079                RegexpMatch match = matches[0];
080    
081                String name = match.getGroup(1);
082                String value = match.getGroup(3);
083                String message = null;
084    
085                int length = match.getMatchLength();
086    
087                if (chopped.length() > length)
088                {
089                    char lastChar = chopped.charAt(length);
090                    if (lastChar == ',')
091                        length--;
092                    else if (lastChar == '[')
093                    {
094                        int messageClose = chopped.indexOf(']', length);
095                        message = chopped.substring(length + 1, messageClose);
096                        length = messageClose;
097                    }
098                }
099    
100                Validator validator = buildValidator(component, name, value, message);
101    
102                result.add(validator);
103    
104                if (length >= chopped.length())
105                    break;
106    
107                chopped = chopped.substring(length + 1);
108    
109            }
110    
111            return Collections.unmodifiableList(result);
112        }
113    
114        private Validator buildValidator(IComponent component, String name, String value, String message)
115        {
116            if (name.startsWith("$"))
117                return extractValidatorBean(component, name, value, message);
118    
119            ValidatorContribution vc = (ValidatorContribution) _validators.get(name);
120    
121            if (vc == null)
122                throw new ApplicationRuntimeException(ValidatorMessages.unknownValidator(name));
123    
124            if (value == null && vc.isConfigurable())
125                throw new ApplicationRuntimeException(ValidatorMessages.needsConfiguration("name"));
126    
127            if (value != null && !vc.isConfigurable())
128                throw new ApplicationRuntimeException(ValidatorMessages.notConfigurable(name, value));
129    
130            try
131            {
132                Object result = vc.getValidatorClass().newInstance();
133    
134                if (vc.isConfigurable())
135                    PropertyUtils.smartWrite(result, name, value);
136    
137                if (message != null)
138                    PropertyUtils.write(result, "message", message);
139    
140                return (Validator) result;
141            }
142            catch (Exception ex)
143            {
144                throw new ApplicationRuntimeException(ValidatorMessages.errorInitializingValidator(
145                        name,
146                        vc.getValidatorClass(),
147                        ex), ex);
148            }
149        }
150    
151        private Validator extractValidatorBean(IComponent component, String validatorName,
152                String value, String message)
153        {
154            String beanName = validatorName.substring(1);
155    
156            if (HiveMind.isNonBlank(value) || HiveMind.isNonBlank(message))
157                throw new ApplicationRuntimeException(ValidatorMessages
158                        .noValueOrMessageForBean(beanName));
159    
160            return new BeanValidatorWrapper(component, beanName);
161        }
162    
163        public void setValidators(Map validators)
164        {
165            _validators = validators;
166        }
167    }