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    
023    import java.util.ArrayList;
024    import java.util.Collections;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.regex.Matcher;
028    
029    /**
030     * Implementation of the tapestry.form.validator.ValidatorFactory service, which builds and caches
031     * validators and lists of validators from a "magic" string specification.
032     * 
033     * @author Howard Lewis Ship
034     * @since 4.0
035     */
036    public class ValidatorFactoryImpl implements ValidatorFactory
037    {
038        private static final String PATTERN = "^\\s*(\\$?\\w+)\\s*(=\\s*(((?!,|\\[).)*))?";
039        private static final java.util.regex.Pattern PATTERN_COMPILED = java.util.regex.Pattern.compile(PATTERN);
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            while (true)
058            {
059                if (chopped.length() == 0)
060                    break;
061    
062                if (!result.isEmpty())
063                {
064                    if (chopped.charAt(0) != ',')
065                        failBadSpec(specification);
066    
067                    chopped = chopped.substring(1);
068                }
069    
070                Matcher matcher = PATTERN_COMPILED.matcher(chopped);
071    
072                if (!matcher.find()) failBadSpec(specification);
073    
074                String name = matcher.group(1);
075                String value = matcher.group(3);
076                String message = null;
077    
078                int length = matcher.group().length();
079    
080                if (matcher.find()) failBadSpec(specification);
081    
082                if (chopped.length() > length)
083                {
084                    char lastChar = chopped.charAt(length);
085                    if (lastChar == ',')
086                        length--;
087                    else if (lastChar == '[')
088                    {
089                        int messageClose = chopped.indexOf(']', length);
090                        message = chopped.substring(length + 1, messageClose);
091                        length = messageClose;
092                    }
093                }
094    
095                Validator validator = buildValidator(component, name, value, message);
096    
097                result.add(validator);
098    
099                if (length >= chopped.length())
100                    break;
101    
102                chopped = chopped.substring(length + 1);
103    
104            }
105    
106            return Collections.unmodifiableList(result);
107        }
108    
109        private void failBadSpec(String specification) {
110            throw new ApplicationRuntimeException(ValidatorMessages.badSpecification(specification));
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    }