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 }