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    
015    package org.apache.tapestry.valid;
016    
017    import java.net.MalformedURLException;
018    import java.net.URL;
019    import java.util.Collection;
020    import java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.Locale;
023    import java.util.Map;
024    import java.util.ResourceBundle;
025    import java.util.Vector;
026    
027    import org.apache.tapestry.IMarkupWriter;
028    import org.apache.tapestry.IRequestCycle;
029    import org.apache.tapestry.form.IFormComponent;
030    import org.apache.tapestry.util.StringSplitter;
031    
032    /**
033     * @since 3.0
034     */
035    public 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    }