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.text.MessageFormat; 018import java.util.HashMap; 019import java.util.Locale; 020import java.util.Map; 021import java.util.ResourceBundle; 022 023import org.apache.hivemind.HiveMind; 024import org.apache.hivemind.Resource; 025import org.apache.hivemind.util.ClasspathResource; 026import org.apache.hivemind.util.PropertyUtils; 027import org.apache.tapestry.IEngine; 028import org.apache.tapestry.IForm; 029import org.apache.tapestry.IMarkupWriter; 030import org.apache.tapestry.IRequestCycle; 031import org.apache.tapestry.IScript; 032import org.apache.tapestry.PageRenderSupport; 033import org.apache.tapestry.TapestryUtils; 034import org.apache.tapestry.engine.IScriptSource; 035import org.apache.tapestry.form.IFormComponent; 036 037/** 038 * Abstract base class for {@link IValidator}. Supports a required and locale property. 039 * 040 * @author Howard Lewis Ship 041 * @since 1.0.8 042 */ 043 044public abstract class BaseValidator implements IValidator 045{ 046 /** 047 * Input Symbol used to represent the field being validated. 048 * 049 * @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map) 050 * @since 2.2 051 */ 052 053 public static final String FIELD_SYMBOL = "field"; 054 055 /** 056 * Input symbol used to represent the validator itself to the script. 057 * 058 * @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map) 059 * @since 2.2 060 */ 061 062 public static final String VALIDATOR_SYMBOL = "validator"; 063 064 /** 065 * Input symbol used to represent the {@link IForm}containing the field to the script. 066 * 067 * @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map) 068 * @since 2.2 069 */ 070 071 public static final String FORM_SYMBOL = "form"; 072 073 /** 074 * Output symbol set by the script asthe name of the validator JavaScript function. The function 075 * implemented must return true or false (true if the field is valid, false otherwise). After 076 * the script is executed, the function is added to the {@link IForm}as a 077 * {@link org.apache.tapestry.form.FormEventType#SUBMIT}. 078 * 079 * @see #processValidatorScript(String, IRequestCycle, IFormComponent, Map) 080 * @since 2.2 081 */ 082 083 public static final String FUNCTION_SYMBOL = "function"; 084 085 private boolean _required; 086 087 /** @since 3.0 */ 088 089 private String _requiredMessage; 090 091 /** 092 * @since 2.2 093 */ 094 095 private boolean _clientScriptingEnabled = false; 096 097 /** 098 * Standard constructor. Leaves locale as system default and required as false. 099 */ 100 101 public BaseValidator() 102 { 103 } 104 105 /** 106 * Allow the validator to be initialized with a property initialization string. 107 * 108 * @since 4.0 109 */ 110 public BaseValidator(String initializer) 111 { 112 PropertyUtils.configureProperties(this, initializer); 113 } 114 115 protected BaseValidator(boolean required) 116 { 117 _required = required; 118 } 119 120 public boolean isRequired() 121 { 122 return _required; 123 } 124 125 public void setRequired(boolean required) 126 { 127 _required = required; 128 } 129 130 /** 131 * Gets a pattern, either as the default value, or as a localized key. If override is null, then 132 * the key from the <code>org.apache.tapestry.valid.ValidationStrings</code> 133 * {@link ResourceBundle}(in the specified locale) is used. The pattern can then be used with 134 * {@link #formatString(String, Object[])}. 135 * <p> 136 * Why do we not just lump these strings into TapestryStrings.properties? because 137 * TapestryStrings.properties is localized to the server's locale, which is fine for the 138 * logging, debugging and error messages it contains. For field validation, whose errors are 139 * visible to the end user normally, we want to localize to the page's locale. 140 * 141 * @param override 142 * The override value for the localized string from the bundle. 143 * @param key 144 * used to lookup pattern from bundle, if override is null. 145 * @param locale 146 * used to get right localization of bundle. 147 * @since 3.0 148 */ 149 150 protected String getPattern(String override, String key, Locale locale) 151 { 152 if (override != null) 153 return override; 154 155 ResourceBundle strings = ResourceBundle.getBundle( 156 "org.apache.tapestry.valid.ValidationStrings", 157 locale); 158 159 return strings.getString(key); 160 } 161 162 /** 163 * Gets a string from the standard resource bundle. The string in the bundle is treated as a 164 * pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}. 165 * 166 * @param pattern 167 * string the input pattern to be used with 168 * {@link MessageFormat#format(java.lang.String, java.lang.Object[])}. It may 169 * contain replaceable parameters, {0}, {1}, etc. 170 * @param args 171 * the arguments used to fill replaceable parameters {0}, {1}, etc. 172 * @since 3.0 173 */ 174 175 protected String formatString(String pattern, Object[] args) 176 { 177 return MessageFormat.format(pattern, args); 178 } 179 180 /** 181 * Convienience method for invoking {@link #formatString(String, Object[])}. 182 * 183 * @since 3.0 184 */ 185 186 protected String formatString(String pattern, Object arg) 187 { 188 return formatString(pattern, new Object[] 189 { arg }); 190 } 191 192 /** 193 * Convienience method for invoking {@link #formatString(String, Object[])}. 194 * 195 * @since 3.0 196 */ 197 198 protected String formatString(String pattern, Object arg1, Object arg2) 199 { 200 return formatString(pattern, new Object[] 201 { arg1, arg2 }); 202 } 203 204 /** 205 * Invoked to check if the value is null. If the value is null (or empty), but the required flag 206 * is set, then this method throws a {@link ValidatorException}. Otherwise, returns true if the 207 * value is null. 208 */ 209 210 protected boolean checkRequired(IFormComponent field, String value) throws ValidatorException 211 { 212 boolean isEmpty = HiveMind.isBlank(value); 213 214 if (_required && isEmpty) 215 throw new ValidatorException(buildRequiredMessage(field), ValidationConstraint.REQUIRED); 216 217 return isEmpty; 218 } 219 220 /** 221 * Builds an error message indicating a value for a required field was not supplied. 222 * 223 * @since 3.0 224 */ 225 226 protected String buildRequiredMessage(IFormComponent field) 227 { 228 String pattern = getPattern(_requiredMessage, "field-is-required", field.getPage() 229 .getLocale()); 230 231 return formatString(pattern, field.getDisplayName()); 232 } 233 234 /** 235 * This implementation does nothing. Subclasses may supply their own implementation. 236 * 237 * @since 2.2 238 */ 239 240 public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer, 241 IRequestCycle cycle) 242 { 243 } 244 245 /** 246 * Invoked (from sub-class implementations of 247 * {@link #renderValidatorContribution(IFormComponent, IMarkupWriter, IRequestCycle)}to process 248 * a standard validation script. This expects that: 249 * <ul> 250 * <li>The {@link IFormComponent}is (ultimately) wrapped by a {@link Body} 251 * <li>The script generates a symbol named "function" (as per {@link #FUNCTION_SYMBOL}) 252 * </ul> 253 * 254 * @param scriptPath 255 * the resource path of the script to execute 256 * @param cycle 257 * The active request cycle 258 * @param field 259 * The field to be validated 260 * @param symbols 261 * a set of input symbols needed by the script. These symbols are augmented with 262 * symbols for the field, form and validator. symbols may be null, but will be 263 * modified if not null. 264 * @throws ApplicationRuntimeException 265 * if there's an error processing the script. 266 * @since 2.2 267 */ 268 269 protected void processValidatorScript(String scriptPath, IRequestCycle cycle, 270 IFormComponent field, Map symbols) 271 { 272 IEngine engine = field.getPage().getEngine(); 273 IScriptSource source = engine.getScriptSource(); 274 IForm form = field.getForm(); 275 276 Map finalSymbols = (symbols == null) ? new HashMap() : symbols; 277 278 finalSymbols.put(FIELD_SYMBOL, field); 279 finalSymbols.put(FORM_SYMBOL, form); 280 finalSymbols.put(VALIDATOR_SYMBOL, this); 281 282 Resource location = new ClasspathResource(engine.getClassResolver(), scriptPath); 283 284 IScript script = source.getScript(location); 285 286 // If there's an error, report it against the field (this validator object doesn't 287 // have a location). 288 289 PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, field); 290 291 script.execute(cycle, pageRenderSupport, finalSymbols); 292 } 293 294 /** 295 * Returns true if client scripting is enabled. Some validators are capable of generating 296 * client-side scripting to perform validation when the form is submitted. By default, this flag 297 * is false and subclasses should check it (in 298 * {@link #renderValidatorContribution(IFormComponent, IMarkupWriter, IRequestCycle)}) before 299 * generating client side script. 300 * 301 * @since 2.2 302 */ 303 304 public boolean isClientScriptingEnabled() 305 { 306 return _clientScriptingEnabled; 307 } 308 309 public void setClientScriptingEnabled(boolean clientScriptingEnabled) 310 { 311 _clientScriptingEnabled = clientScriptingEnabled; 312 } 313 314 public String getRequiredMessage() 315 { 316 return _requiredMessage; 317 } 318 319 /** 320 * Overrides the <code>field-is-required</code> bundle key. Parameter {0} is the display name 321 * of the field. 322 * 323 * @since 3.0 324 */ 325 326 public void setRequiredMessage(String string) 327 { 328 _requiredMessage = string; 329 } 330 331}