001// Copyright 2004, 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.valid;
016
017import java.net.MalformedURLException;
018import java.net.URL;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.Iterator;
022import java.util.Locale;
023import java.util.Map;
024import java.util.ResourceBundle;
025import java.util.Vector;
026
027import org.apache.tapestry.IMarkupWriter;
028import org.apache.tapestry.IRequestCycle;
029import org.apache.tapestry.form.IFormComponent;
030import org.apache.tapestry.util.StringSplitter;
031
032/**
033 * @since 3.0
034 */
035public class UrlValidator extends BaseValidator
036{
037    private int _minimumLength;
038
039    private String _minimumLengthMessage;
040
041    private String _invalidUrlFormatMessage;
042
043    private String _disallowedProtocolMessage;
044
045    private Collection _allowedProtocols;
046
047    private String _scriptPath = "/org/apache/tapestry/valid/UrlValidator.script"; //$NON-NLS-1$
048
049    public UrlValidator()
050    {
051    }
052
053    /**
054     * Initializes the UrlValidator with properties defined by the initializer.
055     * 
056     * @since 4.0
057     */
058
059    public UrlValidator(String initializer)
060    {
061        super(initializer);
062    }
063
064    public String toString(IFormComponent field, Object value)
065    {
066        if (value == null)
067            return null;
068
069        return value.toString();
070    }
071
072    public Object toObject(IFormComponent field, String input) throws ValidatorException
073    {
074        if (checkRequired(field, input))
075            return null;
076
077        if (_minimumLength > 0 && input.length() < _minimumLength)
078            throw new ValidatorException(buildMinimumLengthMessage(field),
079                    ValidationConstraint.MINIMUM_WIDTH);
080
081        if (!isValidUrl(input))
082            throw new ValidatorException(buildInvalidUrlFormatMessage(field),
083                    ValidationConstraint.URL_FORMAT);
084
085        if (!isAllowedProtocol(input))
086        {
087            throw new ValidatorException(buildDisallowedProtocolMessage(field),
088                    ValidationConstraint.DISALLOWED_PROTOCOL);
089        }
090
091        return input;
092    }
093
094    public int getMinimumLength()
095    {
096        return _minimumLength;
097    }
098
099    public void setMinimumLength(int minimumLength)
100    {
101        _minimumLength = minimumLength;
102    }
103
104    public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer,
105            IRequestCycle cycle)
106    {
107        if (!isClientScriptingEnabled())
108            return;
109
110        Map symbols = new HashMap();
111
112        if (isRequired())
113            symbols.put("requiredMessage", buildRequiredMessage(field)); //$NON-NLS-1$
114
115        if (_minimumLength > 0)
116            symbols.put("minimumLengthMessage", //$NON-NLS-1$
117                    buildMinimumLengthMessage(field));
118
119        symbols.put("urlFormatMessage", buildInvalidUrlFormatMessage(field)); //$NON-NLS-1$
120
121        symbols.put("urlDisallowedProtocolMessage", //$NON-NLS-1$
122                buildDisallowedProtocolMessage(field));
123
124        symbols.put("urlRegexpProtocols", buildUrlRegexpProtocols()); //$NON-NLS-1$
125
126        processValidatorScript(_scriptPath, cycle, field, symbols);
127    }
128
129    private String buildUrlRegexpProtocols()
130    {
131        if (_allowedProtocols == null)
132        {
133            return null;
134        }
135        String regexp = "/("; //$NON-NLS-1$
136        Iterator iter = _allowedProtocols.iterator();
137        while (iter.hasNext())
138        {
139            String protocol = (String) iter.next();
140            regexp += protocol;
141            if (iter.hasNext())
142            {
143                regexp += "|"; //$NON-NLS-1$
144            }
145        }
146        regexp += "):///"; //$NON-NLS-1$
147        return regexp;
148    }
149
150    public String getScriptPath()
151    {
152        return _scriptPath;
153    }
154
155    public void setScriptPath(String scriptPath)
156    {
157        _scriptPath = scriptPath;
158    }
159
160    protected boolean isValidUrl(String url)
161    {
162        boolean bIsValid;
163        try
164        {
165            new URL(url);
166            bIsValid = true;
167        }
168        catch (MalformedURLException mue)
169        {
170            bIsValid = false;
171        }
172        return bIsValid;
173    }
174
175    protected boolean isAllowedProtocol(String url)
176    {
177        boolean bIsAllowed = false;
178        if (_allowedProtocols != null)
179        {
180            URL oUrl;
181            try
182            {
183                oUrl = new URL(url);
184            }
185            catch (MalformedURLException e)
186            {
187                return false;
188            }
189            String actualProtocol = oUrl.getProtocol();
190            Iterator iter = _allowedProtocols.iterator();
191            while (iter.hasNext())
192            {
193                String protocol = (String) iter.next();
194                if (protocol.equals(actualProtocol))
195                {
196                    bIsAllowed = true;
197                    break;
198                }
199            }
200        }
201        else
202        {
203            bIsAllowed = true;
204        }
205        return bIsAllowed;
206    }
207
208    public String getInvalidUrlFormatMessage()
209    {
210        return _invalidUrlFormatMessage;
211    }
212
213    public String getMinimumLengthMessage()
214    {
215        return _minimumLengthMessage;
216    }
217
218    public void setInvalidUrlFormatMessage(String string)
219    {
220        _invalidUrlFormatMessage = string;
221    }
222
223    public String getDisallowedProtocolMessage()
224    {
225        return _disallowedProtocolMessage;
226    }
227
228    public void setDisallowedProtocolMessage(String string)
229    {
230        _disallowedProtocolMessage = string;
231    }
232
233    public void setMinimumLengthMessage(String string)
234    {
235        _minimumLengthMessage = string;
236    }
237
238    protected String buildMinimumLengthMessage(IFormComponent field)
239    {
240        String pattern = getPattern(_minimumLengthMessage, "field-too-short", //$NON-NLS-1$
241                field.getPage().getLocale());
242
243        return formatString(pattern, Integer.toString(_minimumLength), field.getDisplayName());
244    }
245
246    protected String buildInvalidUrlFormatMessage(IFormComponent field)
247    {
248        String pattern = getPattern(_invalidUrlFormatMessage, "invalid-url-format", //$NON-NLS-1$
249                field.getPage().getLocale());
250
251        return formatString(pattern, field.getDisplayName());
252    }
253
254    protected String buildDisallowedProtocolMessage(IFormComponent field)
255    {
256        if (_allowedProtocols == null)
257        {
258            return null;
259        }
260        String pattern = getPattern(_disallowedProtocolMessage, "disallowed-protocol", //$NON-NLS-1$
261                field.getPage().getLocale());
262
263        String allowedProtocols = ""; //$NON-NLS-1$
264        Iterator iter = _allowedProtocols.iterator();
265        while (iter.hasNext())
266        {
267            String protocol = (String) iter.next();
268            if (!allowedProtocols.equals("")) { //$NON-NLS-1$
269                if (iter.hasNext())
270                {
271                    allowedProtocols += ", "; //$NON-NLS-1$
272                }
273                else
274                {
275                    allowedProtocols += " or "; //$NON-NLS-1$
276                }
277            }
278            allowedProtocols += protocol;
279        }
280
281        return formatString(pattern, allowedProtocols);
282    }
283
284    protected String getPattern(String override, String key, Locale locale)
285    {
286        if (override != null)
287            return override;
288
289        ResourceBundle strings = ResourceBundle.getBundle(
290                "org.apache.tapestry.valid.ValidationStrings",
291                locale);
292        return strings.getString(key);
293    }
294
295    /**
296     * @param protocols
297     *            comma separated list of allowed protocols
298     */
299    public void setAllowedProtocols(String protocols)
300    {
301        StringSplitter spliter = new StringSplitter(',');
302        // String[] aProtocols = protocols.split(","); //$NON-NLS-1$
303        String[] aProtocols = spliter.splitToArray(protocols); //$NON-NLS-1$
304        _allowedProtocols = new Vector();
305        for (int i = 0; i < aProtocols.length; i++)
306        {
307            _allowedProtocols.add(aProtocols[i]);
308        }
309    }
310
311}