Clover coverage report - Code Coverage for tapestry release 4.0.1
Coverage timestamp: Fri Mar 31 2006 09:12:14 EST
file stats: LOC: 472   Methods: 29
NCLOC: 238   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ValidationDelegate.java 78.3% 83.2% 86.2% 82.4%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.valid;
 16   
 17    import java.util.ArrayList;
 18    import java.util.Collections;
 19    import java.util.HashMap;
 20    import java.util.Iterator;
 21    import java.util.List;
 22    import java.util.Map;
 23   
 24    import org.apache.tapestry.IMarkupWriter;
 25    import org.apache.tapestry.IRender;
 26    import org.apache.tapestry.IRequestCycle;
 27    import org.apache.tapestry.Tapestry;
 28    import org.apache.tapestry.form.IFormComponent;
 29   
 30    /**
 31    * A base implementation of {@link IValidationDelegate}that can be used as a managed bean. This
 32    * class is often subclassed, typically to override presentation details.
 33    *
 34    * @author Howard Lewis Ship
 35    * @since 1.0.5
 36    */
 37   
 38    public class ValidationDelegate implements IValidationDelegate
 39    {
 40    private static final long serialVersionUID = 6215074338439140780L;
 41   
 42    private transient IFormComponent _currentComponent;
 43   
 44    private transient String _focusField;
 45   
 46    private transient int _focusPriority = -1;
 47   
 48    /**
 49    * A list of {@link IFieldTracking}.
 50    */
 51   
 52    private final List _trackings = new ArrayList();
 53   
 54    /**
 55    * A map of {@link IFieldTracking}, keyed on form element name.
 56    */
 57   
 58    private final Map _trackingMap = new HashMap();
 59   
 60  52 public void clear()
 61    {
 62  52 _currentComponent = null;
 63  52 _trackings.clear();
 64  52 _trackingMap.clear();
 65    }
 66   
 67  2 public void clearErrors()
 68    {
 69  2 if (_trackings == null)
 70  0 return;
 71   
 72  2 Iterator i = _trackings.iterator();
 73  2 while (i.hasNext())
 74    {
 75  2 FieldTracking ft = (FieldTracking) i.next();
 76  2 ft.setErrorRenderer(null);
 77    }
 78    }
 79   
 80    /**
 81    * If the form component is in error, places a <font color="red"< around it. Note: this
 82    * will only work on the render phase after a rewind, and will be confused if components are
 83    * inside any kind of loop.
 84    */
 85   
 86  0 public void writeLabelPrefix(IFormComponent component, IMarkupWriter writer, IRequestCycle cycle)
 87    {
 88  0 if (isInError(component))
 89    {
 90  0 writer.begin("font");
 91  0 writer.attribute("color", "red");
 92    }
 93    }
 94   
 95    /**
 96    * Does nothing by default.
 97    * {@inheritDoc}
 98    */
 99   
 100  10 public void writeLabelAttributes(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component) {
 101    }
 102   
 103    /**
 104    * Closes the <font> element,started by
 105    * {@link #writeLabelPrefix(IFormComponent,IMarkupWriter,IRequestCycle)}, if the form component
 106    * is in error.
 107    */
 108   
 109  0 public void writeLabelSuffix(IFormComponent component, IMarkupWriter writer, IRequestCycle cycle)
 110    {
 111  0 if (isInError(component))
 112    {
 113  0 writer.end();
 114    }
 115    }
 116   
 117    /**
 118    * Returns the {@link IFieldTracking}for the current component, if any. The
 119    * {@link IFieldTracking}is usually created in {@link #record(String, ValidationConstraint)}or
 120    * in {@link #record(IRender, ValidationConstraint)}.
 121    * <p>
 122    * Components may be rendered multiple times, with multiple names (provided by the
 123    * {@link org.apache.tapestry.form.Form}, care must be taken that this method is invoked
 124    * <em>after</em> the Form has provided a unique {@link IFormComponent#getName()}for the
 125    * component.
 126    *
 127    * @see #setFormComponent(IFormComponent)
 128    * @return the {@link FieldTracking}, or null if the field has no tracking.
 129    */
 130   
 131  110 protected FieldTracking getComponentTracking()
 132    {
 133  110 return (FieldTracking) _trackingMap.get(_currentComponent.getName());
 134    }
 135   
 136  336 public void setFormComponent(IFormComponent component)
 137    {
 138  336 _currentComponent = component;
 139    }
 140   
 141  58 public boolean isInError()
 142    {
 143  58 IFieldTracking tracking = getComponentTracking();
 144   
 145  58 return tracking != null && tracking.isInError();
 146    }
 147   
 148  2 public String getFieldInputValue()
 149    {
 150  2 IFieldTracking tracking = getComponentTracking();
 151   
 152  2 return tracking == null ? null : tracking.getInput();
 153    }
 154   
 155    /**
 156    * Returns all the field trackings as an unmodifiable List.
 157    */
 158   
 159  16 public List getFieldTracking()
 160    {
 161  16 if (Tapestry.size(_trackings) == 0)
 162  2 return null;
 163   
 164  14 return Collections.unmodifiableList(_trackings);
 165    }
 166   
 167    /** @since 3.0.2 */
 168  0 public IFieldTracking getCurrentFieldTracking()
 169    {
 170  0 return findCurrentTracking();
 171    }
 172   
 173  6 public void reset()
 174    {
 175  6 IFieldTracking tracking = getComponentTracking();
 176   
 177  6 if (tracking != null)
 178    {
 179  6 _trackings.remove(tracking);
 180  6 _trackingMap.remove(tracking.getFieldName());
 181    }
 182    }
 183   
 184    /**
 185    * Invokes {@link #record(String, ValidationConstraint)}, or
 186    * {@link #record(IRender, ValidationConstraint)}if the
 187    * {@link ValidatorException#getErrorRenderer() error renderer property}is not null.
 188    */
 189   
 190  16 public void record(ValidatorException ex)
 191    {
 192  16 IRender errorRenderer = ex.getErrorRenderer();
 193   
 194  16 if (errorRenderer == null)
 195  14 record(ex.getMessage(), ex.getConstraint());
 196    else
 197  2 record(errorRenderer, ex.getConstraint());
 198    }
 199   
 200    /**
 201    * Invokes {@link #record(IRender, ValidationConstraint)}, after wrapping the message parameter
 202    * in a {@link RenderString}.
 203    */
 204   
 205  22 public void record(String message, ValidationConstraint constraint)
 206    {
 207  22 record(new RenderString(message), constraint);
 208    }
 209   
 210    /**
 211    * Records error information about the currently selected component, or records unassociated
 212    * (with any field) errors.
 213    * <p>
 214    * Currently, you may have at most one error per <em>field</em> (note the difference between
 215    * field and component), but any number of unassociated errors.
 216    * <p>
 217    * Subclasses may override the default error message (based on other factors, such as the field
 218    * and constraint) before invoking this implementation.
 219    *
 220    * @since 1.0.9
 221    */
 222   
 223  26 public void record(IRender errorRenderer, ValidationConstraint constraint)
 224    {
 225  26 FieldTracking tracking = findCurrentTracking();
 226   
 227    // Note that recording two errors for the same field is not advised; the
 228    // second will override the first.
 229   
 230  26 tracking.setErrorRenderer(errorRenderer);
 231  26 tracking.setConstraint(constraint);
 232    }
 233   
 234    /** @since 4.0 */
 235   
 236  4 public void record(IFormComponent field, String message)
 237    {
 238  4 setFormComponent(field);
 239   
 240  4 record(message, null);
 241    }
 242   
 243  22 public void recordFieldInputValue(String input)
 244    {
 245  22 FieldTracking tracking = findCurrentTracking();
 246   
 247  22 tracking.setInput(input);
 248    }
 249   
 250    /**
 251    * Finds or creates the field tracking for the {@link #setFormComponent(IFormComponent)}
 252    * &nbsp;current component. If no current component, an unassociated error is created and
 253    * returned.
 254    *
 255    * @since 3.0
 256    */
 257   
 258  48 protected FieldTracking findCurrentTracking()
 259    {
 260  48 FieldTracking result = null;
 261   
 262  48 if (_currentComponent == null)
 263    {
 264  4 result = new FieldTracking();
 265   
 266    // Add it to the field trackings, but not to the
 267    // map.
 268   
 269  4 _trackings.add(result);
 270    }
 271    else
 272    {
 273  44 result = getComponentTracking();
 274   
 275  44 if (result == null)
 276    {
 277  28 String fieldName = _currentComponent.getName();
 278   
 279  28 result = new FieldTracking(fieldName, _currentComponent);
 280   
 281  28 _trackings.add(result);
 282  28 _trackingMap.put(fieldName, result);
 283    }
 284    }
 285   
 286  48 return result;
 287    }
 288   
 289    /**
 290    * Does nothing. Override in a subclass to decoreate fields.
 291    */
 292   
 293  26 public void writePrefix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component,
 294    IValidator validator)
 295    {
 296    }
 297   
 298    /**
 299    * Does nothing. Override in a subclass to decorate fields.
 300    */
 301   
 302  26 public void writeAttributes(IMarkupWriter writer, IRequestCycle cycle,
 303    IFormComponent component, IValidator validator)
 304    {
 305    }
 306   
 307    /**
 308    * Default implementation; if the current field is in error, then a suffix is written. The
 309    * suffix is: <code>&amp;nbsp;&lt;font color="red"&gt;**&lt;/font&gt;</code>.
 310    */
 311   
 312  24 public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component,
 313    IValidator validator)
 314    {
 315  24 if (isInError())
 316    {
 317  0 writer.printRaw("&nbsp;");
 318  0 writer.begin("font");
 319  0 writer.attribute("color", "red");
 320  0 writer.print("**");
 321  0 writer.end();
 322    }
 323    }
 324   
 325  64 public boolean getHasErrors()
 326    {
 327  64 return getFirstError() != null;
 328    }
 329   
 330    /**
 331    * A convienience, as most pages just show the first error on the page.
 332    * <p>
 333    * As of release 1.0.9, this returns an instance of {@link IRender}, not a {@link String}.
 334    */
 335   
 336  78 public IRender getFirstError()
 337    {
 338  78 if (Tapestry.size(_trackings) == 0)
 339  56 return null;
 340   
 341  22 Iterator i = _trackings.iterator();
 342   
 343  22 while (i.hasNext())
 344    {
 345  26 IFieldTracking tracking = (IFieldTracking) i.next();
 346   
 347  26 if (tracking.isInError())
 348  16 return tracking.getErrorRenderer();
 349    }
 350   
 351  6 return null;
 352    }
 353   
 354    /**
 355    * Checks to see if the field is in error. This will <em>not</em> work properly in a loop, but
 356    * is only used by {@link FieldLabel}. Therefore, using {@link FieldLabel}in a loop (where the
 357    * {@link IFormComponent}is renderred more than once) will not provide correct results.
 358    */
 359   
 360  0 protected boolean isInError(IFormComponent component)
 361    {
 362    // Get the name as most recently rendered.
 363   
 364  0 String fieldName = component.getName();
 365   
 366  0 IFieldTracking tracking = (IFieldTracking) _trackingMap.get(fieldName);
 367   
 368  0 return tracking != null && tracking.isInError();
 369    }
 370   
 371    /**
 372    * Returns a {@link List}of {@link IFieldTracking}s. This is the master list of trackings,
 373    * except that it omits and trackings that are not associated with a particular field. May
 374    * return an empty list, or null.
 375    * <p>
 376    * Order is not determined, though it is likely the order in which components are laid out on in
 377    * the template (this is subject to change).
 378    */
 379   
 380  2 public List getAssociatedTrackings()
 381    {
 382  2 int count = Tapestry.size(_trackings);
 383   
 384  2 if (count == 0)
 385  0 return null;
 386   
 387  2 List result = new ArrayList(count);
 388   
 389  2 for (int i = 0; i < count; i++)
 390    {
 391  4 IFieldTracking tracking = (IFieldTracking) _trackings.get(i);
 392   
 393  4 if (tracking.getFieldName() == null)
 394  2 continue;
 395   
 396  2 result.add(tracking);
 397    }
 398   
 399  2 return result;
 400    }
 401   
 402    /**
 403    * Like {@link #getAssociatedTrackings()}, but returns only the unassociated trackings.
 404    * Unassociated trackings are new (in release 1.0.9), and are why interface
 405    * {@link IFieldTracking}is not very well named.
 406    * <p>
 407    * The trackings are returned in an unspecified order, which (for the moment, anyway) is the
 408    * order in which they were added (this could change in the future, or become more concrete).
 409    */
 410   
 411  4 public List getUnassociatedTrackings()
 412    {
 413  4 int count = Tapestry.size(_trackings);
 414   
 415  4 if (count == 0)
 416  0 return null;
 417   
 418  4 List result = new ArrayList(count);
 419   
 420  4 for (int i = 0; i < count; i++)
 421    {
 422  6 IFieldTracking tracking = (IFieldTracking) _trackings.get(i);
 423   
 424  6 if (tracking.getFieldName() != null)
 425  2 continue;
 426   
 427  4 result.add(tracking);
 428    }
 429   
 430  4 return result;
 431    }
 432   
 433  4 public List getErrorRenderers()
 434    {
 435  4 List result = new ArrayList();
 436   
 437  4 Iterator i = _trackings.iterator();
 438  4 while (i.hasNext())
 439    {
 440  4 IFieldTracking tracking = (IFieldTracking) i.next();
 441   
 442  4 IRender errorRenderer = tracking.getErrorRenderer();
 443   
 444  4 if (errorRenderer != null)
 445  2 result.add(errorRenderer);
 446    }
 447   
 448  4 return result;
 449    }
 450   
 451    /** @since 4.0 */
 452   
 453  40 public void registerForFocus(IFormComponent field, int priority)
 454    {
 455  40 if (priority > _focusPriority)
 456    {
 457  28 _focusField = field.getClientId();
 458  28 _focusPriority = priority;
 459    }
 460    }
 461   
 462    /**
 463    * Returns the focus field, or null if no form components registered for focus (i.e., they were
 464    * all disabled).
 465    */
 466   
 467  88 public String getFocusField()
 468    {
 469  88 return _focusField;
 470    }
 471   
 472    }