Clover coverage report - Code Coverage for tapestry release 4.0-beta-10
Coverage timestamp: Sat Oct 8 2005 19:08:05 EDT
file stats: LOC: 464   Methods: 28
NCLOC: 236   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ValidationDelegate.java 78.3% 83.2% 85.7% 82.3%
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  55 protected FieldTracking getComponentTracking()
 124    {
 125  55 return (FieldTracking) _trackingMap.get(_currentComponent.getName());
 126    }
 127   
 128  168 public void setFormComponent(IFormComponent component)
 129    {
 130  168 _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  8 public List getFieldTracking()
 152    {
 153  8 if (Tapestry.size(_trackings) == 0)
 154  1 return null;
 155   
 156  7 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  11 public void record(String message, ValidationConstraint constraint)
 198    {
 199  11 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  13 public void record(IRender errorRenderer, ValidationConstraint constraint)
 216    {
 217  13 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  13 tracking.setErrorRenderer(errorRenderer);
 223  13 tracking.setConstraint(constraint);
 224    }
 225   
 226    /** @since 4.0 */
 227   
 228  2 public void record(IFormComponent field, String message)
 229    {
 230  2 setFormComponent(field);
 231   
 232  2 record(message, null);
 233    }
 234   
 235  11 public void recordFieldInputValue(String input)
 236    {
 237  11 FieldTracking tracking = findCurrentTracking();
 238   
 239  11 tracking.setInput(input);
 240    }
 241   
 242    /**
 243    * Finds or creates the field tracking for the {@link #setFormComponent(IFormComponent)}
 244    * &nbsp;current component. If no current component, an unassociated error is created and
 245    * returned.
 246    *
 247    * @since 3.0
 248    */
 249   
 250  24 protected FieldTracking findCurrentTracking()
 251    {
 252  24 FieldTracking result = null;
 253   
 254  24 if (_currentComponent == null)
 255    {
 256  2 result = new FieldTracking();
 257   
 258    // Add it to the field trackings, but not to the
 259    // map.
 260   
 261  2 _trackings.add(result);
 262    }
 263    else
 264    {
 265  22 result = getComponentTracking();
 266   
 267  22 if (result == null)
 268    {
 269  14 String fieldName = _currentComponent.getName();
 270   
 271  14 result = new FieldTracking(fieldName, _currentComponent);
 272   
 273  14 _trackings.add(result);
 274  14 _trackingMap.put(fieldName, result);
 275    }
 276    }
 277   
 278  24 return result;
 279    }
 280   
 281    /**
 282    * Does nothing. Override in a subclass to decoreate fields.
 283    */
 284   
 285  13 public void writePrefix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component,
 286    IValidator validator)
 287    {
 288    }
 289   
 290    /**
 291    * Does nothing. Override in a subclass to decorate fields.
 292    */
 293   
 294  13 public void writeAttributes(IMarkupWriter writer, IRequestCycle cycle,
 295    IFormComponent component, IValidator validator)
 296    {
 297    }
 298   
 299    /**
 300    * Default implementation; if the current field is in error, then a suffix is written. The
 301    * suffix is: <code>&amp;nbsp;&lt;font color="red"&gt;**&lt;/font&gt;</code>.
 302    */
 303   
 304  12 public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component,
 305    IValidator validator)
 306    {
 307  12 if (isInError())
 308    {
 309  0 writer.printRaw("&nbsp;");
 310  0 writer.begin("font");
 311  0 writer.attribute("color", "red");
 312  0 writer.print("**");
 313  0 writer.end();
 314    }
 315    }
 316   
 317  32 public boolean getHasErrors()
 318    {
 319  32 return getFirstError() != null;
 320    }
 321   
 322    /**
 323    * A convienience, as most pages just show the first error on the page.
 324    * <p>
 325    * As of release 1.0.9, this returns an instance of {@link IRender}, not a {@link String}.
 326    */
 327   
 328  39 public IRender getFirstError()
 329    {
 330  39 if (Tapestry.size(_trackings) == 0)
 331  28 return null;
 332   
 333  11 Iterator i = _trackings.iterator();
 334   
 335  11 while (i.hasNext())
 336    {
 337  13 IFieldTracking tracking = (IFieldTracking) i.next();
 338   
 339  13 if (tracking.isInError())
 340  8 return tracking.getErrorRenderer();
 341    }
 342   
 343  3 return null;
 344    }
 345   
 346    /**
 347    * Checks to see if the field is in error. This will <em>not</em> work properly in a loop, but
 348    * is only used by {@link FieldLabel}. Therefore, using {@link FieldLabel}in a loop (where the
 349    * {@link IFormComponent}is renderred more than once) will not provide correct results.
 350    */
 351   
 352  0 protected boolean isInError(IFormComponent component)
 353    {
 354    // Get the name as most recently rendered.
 355   
 356  0 String fieldName = component.getName();
 357   
 358  0 IFieldTracking tracking = (IFieldTracking) _trackingMap.get(fieldName);
 359   
 360  0 return tracking != null && tracking.isInError();
 361    }
 362   
 363    /**
 364    * Returns a {@link List}of {@link IFieldTracking}s. This is the master list of trackings,
 365    * except that it omits and trackings that are not associated with a particular field. May
 366    * return an empty list, or null.
 367    * <p>
 368    * Order is not determined, though it is likely the order in which components are laid out on in
 369    * the template (this is subject to change).
 370    */
 371   
 372  1 public List getAssociatedTrackings()
 373    {
 374  1 int count = Tapestry.size(_trackings);
 375   
 376  1 if (count == 0)
 377  0 return null;
 378   
 379  1 List result = new ArrayList(count);
 380   
 381  1 for (int i = 0; i < count; i++)
 382    {
 383  2 IFieldTracking tracking = (IFieldTracking) _trackings.get(i);
 384   
 385  2 if (tracking.getFieldName() == null)
 386  1 continue;
 387   
 388  1 result.add(tracking);
 389    }
 390   
 391  1 return result;
 392    }
 393   
 394    /**
 395    * Like {@link #getAssociatedTrackings()}, but returns only the unassociated trackings.
 396    * Unassociated trackings are new (in release 1.0.9), and are why interface
 397    * {@link IFieldTracking}is not very well named.
 398    * <p>
 399    * The trackings are returned in an unspecified order, which (for the moment, anyway) is the
 400    * order in which they were added (this could change in the future, or become more concrete).
 401    */
 402   
 403  2 public List getUnassociatedTrackings()
 404    {
 405  2 int count = Tapestry.size(_trackings);
 406   
 407  2 if (count == 0)
 408  0 return null;
 409   
 410  2 List result = new ArrayList(count);
 411   
 412  2 for (int i = 0; i < count; i++)
 413    {
 414  3 IFieldTracking tracking = (IFieldTracking) _trackings.get(i);
 415   
 416  3 if (tracking.getFieldName() != null)
 417  1 continue;
 418   
 419  2 result.add(tracking);
 420    }
 421   
 422  2 return result;
 423    }
 424   
 425  2 public List getErrorRenderers()
 426    {
 427  2 List result = new ArrayList();
 428   
 429  2 Iterator i = _trackings.iterator();
 430  2 while (i.hasNext())
 431    {
 432  2 IFieldTracking tracking = (IFieldTracking) i.next();
 433   
 434  2 IRender errorRenderer = tracking.getErrorRenderer();
 435   
 436  2 if (errorRenderer != null)
 437  1 result.add(errorRenderer);
 438    }
 439   
 440  2 return result;
 441    }
 442   
 443    /** @since 4.0 */
 444   
 445  20 public void registerForFocus(IFormComponent field, int priority)
 446    {
 447  20 if (priority > _focusPriority)
 448    {
 449  14 _focusField = field.getClientId();
 450  14 _focusPriority = priority;
 451    }
 452    }
 453   
 454    /**
 455    * Returns the focus field, or null if no form components registered for focus (i.e., they were
 456    * all disabled).
 457    */
 458   
 459  44 public String getFocusField()
 460    {
 461  44 return _focusField;
 462    }
 463   
 464    }