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 org.apache.tapestry.IMarkupWriter; 018 import org.apache.tapestry.IRender; 019 import org.apache.tapestry.IRequestCycle; 020 import org.apache.tapestry.form.IFormComponent; 021 import org.apache.tapestry.form.TextField; 022 023 import java.io.Serializable; 024 import java.util.List; 025 026 /** 027 * Interface used to track validation errors in forms and 028 * {@link IFormComponent form element component}s (including 029 * {@link TextField} and its subclasses). 030 * <p> 031 * In addition, controls how fields that are in error are presented (they can be 032 * <em>decorated</em> in various ways by the delegate; the default 033 * implementation adds two red asterisks to the right of the field). 034 * <p> 035 * Each {@link org.apache.tapestry.form.Form} must have its own validation 036 * delegate instance. 037 * <p> 038 * Starting with release 1.0.8, this interface was extensively revised (in a 039 * non-backwards compatible way) to move the tracking of errors and invalid 040 * values (during a request cycle) to the delegate. It has evolved from a 041 * largely stateless conduit for error messages into a very stateful tracker of 042 * field state. 043 * <p> 044 * Starting with release 1.0.9, this interface was <em>again</em> reworked, to 045 * allow tracking of errors in {@link IFormComponent form components}, and to 046 * allow unassociated errors to be tracked. Unassociated errors are "global", 047 * they don't apply to any particular field. 048 * <p> 049 * <b>Fields vs. Form Element Components </b> <br> 050 * For most simple forms, these terms are pretty much synonymous. Your form will 051 * render normally, and each form element component will render only once. Some 052 * of your form components will be {@link ValidField} components and 053 * handle most of their validation internally (with the help of 054 * {@link IValidator} objects). In addition, your form listener may do 055 * additional validation and notify the validation delegate of additional 056 * errors, some of which are associated with a particular field, some of which 057 * are unassociated with any particular field. 058 * <p> 059 * But what happens if you use a {@link org.apache.tapestry.components.ForBean} or 060 * {@link org.apache.tapestry.form.ListEdit} inside your form? Some of 061 * your components will render multiple times. In this case you will have 062 * multiple <em>fields</em>. Each field will have a unique field name (the 063 * {@link org.apache.tapestry.form.FormSupport#getElementId(IFormComponent) element id}, 064 * which you can see this in the generated HTML). It is this field name that the 065 * delegate keys off of, which means that some fields generated by a component 066 * may have errors and some may not, it all works fine (with one exception). 067 * <p> 068 * <b>The Exception </b> <br> 069 * The problem is that a component doesn't know its field name until its 070 * <code>render()</code> method is invoked (at which point, it allocates a 071 * unique field name from the 072 * {@link org.apache.tapestry.IForm#getElementId(org.apache.tapestry.form.IFormComponent)}. 073 * This is not a problem for the field or its {@link IValidator}, but screws 074 * things up for the {@link FieldLabel}. 075 * <p> 076 * Typically, the label is rendered <em>before</em> the corresponding form 077 * component. Form components leave their last assigned field name in their 078 * {@link IFormComponent#getName() name property}. So if the form component is 079 * in any kind of loop, the {@link FieldLabel}will key its name, 080 * {@link IFormComponent#getDisplayName() display name} and error status off of 081 * its last renderred value. So the moral of the story is don't use 082 * {@link FieldLabel}in this situation. 083 * 084 * @author Howard Lewis Ship 085 */ 086 087 public interface IValidationDelegate extends Serializable 088 { 089 090 /** 091 * Invoked before other methods to configure the delegate for the given form 092 * component. Sets the current field based on the 093 * {@link IFormComponent#getName() name} of the form component. 094 * <p> 095 * The caller should invoke this with a parameter of null to record 096 * unassociated global errors (errors not associated with any particular 097 * field). 098 * 099 * @since 1.0.8 100 */ 101 102 void setFormComponent(IFormComponent component); 103 104 /** 105 * Returns true if the current field is in error (that is, had bad input 106 * submitted by the end user). 107 * 108 * @since 1.0.8 109 */ 110 111 boolean isInError(); 112 113 /** 114 * Returns the string submitted by the client as the value for the current 115 * field. 116 * 117 * @since 1.0.8 118 */ 119 120 String getFieldInputValue(); 121 122 /** 123 * Returns a {@link List} of {@link IFieldTracking}, in default order (the 124 * order in which fields are renderred). A caller should not change the 125 * values (the List is immutable). May return null if no fields are in 126 * error. 127 * 128 * @since 1.0.8 129 */ 130 131 List getFieldTracking(); 132 133 /** 134 * Resets any tracking information for the current field. This will clear 135 * the field's inError flag, and set its error message and invalid input 136 * value to null. 137 * 138 * @since 1.0.8 139 */ 140 141 void reset(); 142 143 /** 144 * Clears all tracking information. 145 * 146 * @since 1.0.10 147 */ 148 149 void clear(); 150 151 /** 152 * Clears all errors, but maintains user input. This is useful when a form 153 * has been submitted for a semantic other than "process this data". A 154 * common example of this is a dependent drop down list; selecting an option 155 * in one drop down list forces a refresh submit of the form, to repopulate 156 * the options in a second, dependent drop down list. 157 * <p> 158 * In these cases, the user input provided in the request is maintained, but 159 * any errors should be cleared out (to prevent unwanted error messages and 160 * decorations). 161 * 162 * @since 3.0.1 163 */ 164 165 void clearErrors(); 166 167 /** 168 * Records the user's input for the current form component. Input should be 169 * recorded even if there isn't an explicit error, since later form-wide 170 * validations may discover an error in the field. 171 * 172 * @since 3.0 173 */ 174 175 void recordFieldInputValue(String input); 176 177 /** 178 * The error notification method, invoked during the rewind phase (that is, 179 * while HTTP parameters are being extracted from the request and assigned 180 * to various object properties). 181 * <p> 182 * Typically, the delegate simply invokes 183 * {@link #record(String, ValidationConstraint)}or 184 * {@link #record(IRender, ValidationConstraint)}, but special delegates 185 * may override this behavior to provide (in some cases) different error 186 * messages or more complicated error renderers. 187 */ 188 189 void record(ValidatorException ex); 190 191 /** 192 * Records an error in the current field, or an unassociated error if there 193 * is no current field. 194 * 195 * @param message 196 * message to display (@see RenderString} 197 * @param constraint 198 * the constraint that was violated, or null if not known 199 * @since 1.0.9 200 */ 201 202 void record(String message, ValidationConstraint constraint); 203 204 /** 205 * Convienience for recording a standard string messages against a field. 206 * 207 * @param field 208 * the field to record the error message against, or null to 209 * record an unassociated error 210 * @param message 211 * the error message to record 212 * @since 4.0 213 */ 214 215 void record(IFormComponent field, String message); 216 217 /** 218 * Records an error in the current component, or an unassociated error. The 219 * maximum flexibility recorder. 220 * 221 * @param errorRenderer 222 * object that will render the error message (@see RenderString} 223 * @param constraint 224 * the constraint that was violated, or null if not known 225 */ 226 227 void record(IRender errorRenderer, ValidationConstraint constraint); 228 229 /** 230 * Invoked before the field is rendered. If the field is in error, the 231 * delegate may decorate the field in some way (to highlight its error 232 * state). 233 * 234 * @param writer 235 * the writer to which output should be sent 236 * @param cycle 237 * the active request cycle 238 * @param component 239 * the component being decorated 240 * @param validator 241 * the validator for the component, or null if the component does 242 * have (or doesn't support) a validator 243 */ 244 245 void writePrefix(IMarkupWriter writer, IRequestCycle cycle, 246 IFormComponent component, IValidator validator); 247 248 /** 249 * Invoked just before the <input> element is closed. The delegate can 250 * write additional attributes. This is often used to set the CSS class of 251 * the field so that it can be displayed differently, if in error (or 252 * required). * 253 * 254 * @param writer 255 * the writer to which output should be sent 256 * @param cycle 257 * the active request cycle 258 * @param component 259 * the component being decorated 260 * @param validator 261 * the validator for the component, or null if the component does 262 * have (or doesn't support) a validator 263 * @since 1.0.5 264 */ 265 266 void writeAttributes(IMarkupWriter writer, IRequestCycle cycle, 267 IFormComponent component, IValidator validator); 268 269 /** 270 * Invoked after the form component is rendered, so that the delegate may 271 * decorate the form component (if it is in error). * 272 * 273 * @param writer 274 * the writer to which output should be sent 275 * @param cycle 276 * the active request cycle 277 * @param component 278 * the component being decorated 279 * @param validator 280 * the validator for the component, or null if the component does 281 * have (or doesn't support) a validator 282 */ 283 284 void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, 285 IFormComponent component, IValidator validator); 286 287 /** 288 * Invoked by a {@link FieldLabel} just before writing the name of the form 289 * component. 290 */ 291 292 void writeLabelPrefix(IFormComponent component, 293 IMarkupWriter writer, IRequestCycle cycle); 294 295 /** 296 * Invoked just before the <label> element is closed. The delegate can 297 * write additional attributes. This is often used to set the CSS class of 298 * the label so that it can be displayed differently, if in error (or 299 * required). Any attributes written here will be overriden by any informal 300 * parameters specified in the {@link FieldLabel} implementation. 301 * 302 * @param writer 303 * the writer to which output should be sent 304 * @param cycle 305 * the active request cycle 306 * @param component 307 * the component field that label decorates 308 * @since 4.0.1 309 */ 310 311 void writeLabelAttributes(IMarkupWriter writer, IRequestCycle cycle, 312 IFormComponent component); 313 314 /** 315 * Invoked just before the actual field label text is written, right after all attributes and 316 * informal parameters are done being printed on the <code><label></code> tag. 317 * 318 * <p> 319 * Example, writing content would go here: 320 * </p> 321 * <p> 322 * <label class="error"> >>here<< LABEL TEXT </label> 323 * </p> 324 * 325 * @param writer 326 * The writer to use. 327 * @param cycle 328 * Current request cycle. 329 * @param component 330 * Field label is bound to. 331 */ 332 void beforeLabelText(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component); 333 334 /** 335 * Invoked just before the closing <code></label></code> tag is written. 336 * 337 * <p> 338 * Example, writing content would go here: 339 * </p> 340 * <p> 341 * <label class="error"> LABEL TEXT >>here<< </label> 342 * </p> 343 * 344 * @param writer 345 * The writer to use. 346 * @param cycle 347 * Current request cycle. 348 * @param component 349 * Field label is bound to. 350 */ 351 void afterLabelText(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component); 352 353 /** 354 * Invoked by a {@link FieldLabel} just after writing the name of the form 355 * component. 356 */ 357 358 void writeLabelSuffix(IFormComponent component, IMarkupWriter writer, IRequestCycle cycle); 359 360 /** 361 * Returns true if any form component has errors. 362 */ 363 364 boolean getHasErrors(); 365 366 /** 367 * Returns the {@link IFieldTracking} for the current component, if 368 * any. Useful when displaying error messages for individual fields. 369 * 370 * @since 3.0.2 371 */ 372 IFieldTracking getCurrentFieldTracking(); 373 374 /** 375 * Returns a list of {@link org.apache.tapestry.IRender} objects, each of 376 * which will render an error message for a field tracked by the delegate, 377 * plus any unassociated errors (for which no specific field is identified). 378 * These objects can be rendered or converted to a string (via toString()). 379 * 380 * @return non-empty List of {@link org.apache.tapestry.IRender}. 381 */ 382 383 List getErrorRenderers(); 384 385 /** 386 * Registers a field for automatic focus. The goal is for the first field 387 * that is in error to get focus; failing that, the first required field; 388 * failing that, any field. 389 * 390 * @param field 391 * the field requesting focus 392 * @param priority 393 * a priority level used to determine whether the registered 394 * field becomes the focus field. Constants for this purpose are 395 * defined in {@link ValidationConstants}. 396 * @since 4.0 397 */ 398 399 void registerForFocus(IFormComponent field, int priority); 400 401 /** 402 * Returns the field to focus upon, based on prior calls to 403 * {@link #registerForFocus(IFormComponent, int)}. 404 * 405 * @return the field name, or null if no field should receive focus. 406 * @since 4.0 407 */ 408 String getFocusField(); 409 410 }