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