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 }