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