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