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.util.HashMap;
018 import java.util.Map;
019
020 import org.apache.hivemind.ApplicationRuntimeException;
021 import org.apache.tapestry.IMarkupWriter;
022 import org.apache.tapestry.IRequestCycle;
023 import org.apache.tapestry.Tapestry;
024 import org.apache.tapestry.form.IFormComponent;
025 import org.apache.tapestry.util.RegexpMatcher;
026
027 /**
028 * <p>The validator bean that provides a pattern validation service.
029 *
030 * <p>The actual pattern matching algorithm is provided by the
031 * {@link org.apache.tapestry.valid.PatternDelegate}. This enables the user to provide
032 * custom pattern matching implementations. In the event a custom implementation is not
033 * provided, this validator will use the {@link org.apache.tapestry.util.RegexpMatcher}.
034 *
035 * <p>This validator has the ability to provide client side validation on demand.
036 * To enable client side validation simply set the <code>clientScriptingEnabled</code>
037 * property to <code>true</code>.
038 * The default implementation of the script will be in JavaScript and allows the user to
039 * override this with a custom implementation by setting the path to the custom
040 * script via {@link #setScriptPath(String)}.
041 *
042 * @author Harish Krishnaswamy
043 * @since 3.0
044 */
045 public class PatternValidator extends BaseValidator
046 {
047 /**
048 * The pattern that this validator will use to validate the input. The default
049 * pattern is an empty string.
050 */
051 private String _patternString = "";
052
053 /**
054 * A custom message in the event of a validation failure.
055 */
056 private String _patternNotMatchedMessage;
057
058 /**
059 * The object that handles pattern matching.
060 */
061 private PatternDelegate _patternDelegate;
062
063 /**
064 * The location of the script specification for client side validation.
065 */
066 private String _scriptPath = "/org/apache/tapestry/valid/PatternValidator.script";
067
068 /**
069 * Returns custom validation failure message. The default message comes from
070 * <code>ValidationStrings.properties</code> file for key
071 * <code>pattern-not-matched</code>.
072 */
073 public String getPatternNotMatchedMessage()
074 {
075 return _patternNotMatchedMessage;
076 }
077
078 /**
079 * Returns the pattern that this validator uses for validation.
080 */
081 public String getPatternString()
082 {
083 return _patternString;
084 }
085
086 /**
087 * Allows for a custom message to be set typically via the bean specification.
088 */
089 public void setPatternNotMatchedMessage(String message)
090 {
091 _patternNotMatchedMessage = message;
092 }
093
094 /**
095 * Allows the user to change the validation pattern.
096 */
097 public void setPatternString(String pattern)
098 {
099 _patternString = pattern;
100 }
101
102 /**
103 * Static inner class that acts as a delegate to RegexpMatcher and conforms to the
104 * PatternDelegate contract.
105 */
106 private static class RegExpDelegate implements PatternDelegate
107 {
108 private RegexpMatcher _matcher;
109
110 private RegexpMatcher getPatternMatcher()
111 {
112 if (_matcher == null)
113 _matcher = new RegexpMatcher();
114
115 return _matcher;
116 }
117
118 public boolean contains(String patternString, String input)
119 {
120 return getPatternMatcher().contains(patternString, input);
121 }
122
123 public String getEscapedPatternString(String patternString)
124 {
125 return getPatternMatcher().getEscapedPatternString(patternString);
126 }
127 }
128
129 /**
130 * Allows for a custom implementation to do the pattern matching. The default pattern
131 * matching is done with {@link org.apache.tapestry.util.RegexpMatcher}.
132 */
133 public void setPatternDelegate(PatternDelegate patternDelegate)
134 {
135 _patternDelegate = patternDelegate;
136 }
137
138 /**
139 * Returns the custom pattern matcher if one is provided or creates and returns the
140 * default matcher laziliy.
141 */
142 public PatternDelegate getPatternDelegate()
143 {
144 if (_patternDelegate == null)
145 _patternDelegate = new RegExpDelegate();
146
147 return _patternDelegate;
148 }
149
150 /**
151 * @see org.apache.tapestry.valid.IValidator#toString(org.apache.tapestry.form.IFormComponent, java.lang.Object)
152 */
153 public String toString(IFormComponent field, Object value)
154 {
155 if (value == null)
156 return null;
157
158 return value.toString();
159 }
160
161 private String buildPatternNotMatchedMessage(IFormComponent field, String patternString)
162 {
163 String templateMessage =
164 getPattern(
165 _patternNotMatchedMessage,
166 "pattern-not-matched",
167 field.getPage().getLocale());
168
169 return formatString(templateMessage, field.getDisplayName(), patternString);
170 }
171
172 /**
173 * @see org.apache.tapestry.valid.IValidator#toObject(org.apache.tapestry.form.IFormComponent, java.lang.String)
174 */
175 public Object toObject(IFormComponent field, String input) throws ValidatorException
176 {
177 if (checkRequired(field, input))
178 return null;
179
180 boolean matched = false;
181
182 try
183 {
184 matched = getPatternDelegate().contains(_patternString, input);
185 }
186 catch (Throwable t)
187 {
188 throw new ApplicationRuntimeException(
189 Tapestry.format(
190 "PatternValidator.pattern-match-error",
191 _patternString,
192 field.getDisplayName()),
193 field,
194 field.getLocation(),
195 t);
196 }
197
198 if (!matched)
199 throw new ValidatorException(
200 buildPatternNotMatchedMessage(field, _patternString),
201 ValidationConstraint.PATTERN_MISMATCH);
202
203 return input;
204 }
205
206 /**
207 * Allows for a custom implementation of the client side validation.
208 */
209 public void setScriptPath(String scriptPath)
210 {
211 _scriptPath = scriptPath;
212 }
213
214 /**
215 * @see org.apache.tapestry.valid.IValidator#renderValidatorContribution(org.apache.tapestry.form.IFormComponent, org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
216 */
217 public void renderValidatorContribution(
218 IFormComponent field,
219 IMarkupWriter writer,
220 IRequestCycle cycle)
221 {
222 if (!isClientScriptingEnabled())
223 return;
224
225 Map symbols = new HashMap();
226
227 if (isRequired())
228 symbols.put("requiredMessage", buildRequiredMessage(field));
229
230 symbols.put(
231 "patternNotMatchedMessage",
232 buildPatternNotMatchedMessage(field, getEscapedPatternString()));
233
234 processValidatorScript(_scriptPath, cycle, field, symbols);
235 }
236
237 /**
238 * Returns the escaped sequence of the pattern string for rendering in the error message.
239 */
240 public String getEscapedPatternString()
241 {
242 return getPatternDelegate().getEscapedPatternString(_patternString);
243 }
244
245 public String toString()
246 {
247 return "Pattern: "
248 + _patternString
249 + "; Script Path: "
250 + _scriptPath
251 + "; Pattern Delegate: "
252 + _patternDelegate;
253 }
254 }