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
015package org.apache.tapestry.form.validator;
016
017import java.util.ArrayList;
018import java.util.Collections;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.hivemind.ApplicationRuntimeException;
023import org.apache.hivemind.HiveMind;
024import org.apache.hivemind.util.Defense;
025import org.apache.hivemind.util.PropertyUtils;
026import org.apache.tapestry.IComponent;
027import org.apache.tapestry.util.RegexpMatch;
028import 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 */
037public 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}