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