Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 399   Methods: 23
NCLOC: 193   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
ValidationDelegate.java 88.9% 96.3% 95.7% 94.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   
     /**
 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  36
     public void clear()
 57   
     {
 58  36
         _currentComponent = null;
 59  36
         _trackings.clear();
 60  36
         _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  5
     public void writeLabelPrefix(IFormComponent component, IMarkupWriter writer, IRequestCycle cycle)
 70   
     {
 71  5
         if (isInError(component))
 72   
         {
 73  1
             writer.begin("font");
 74  1
             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  5
     public void writeLabelSuffix(IFormComponent component, IMarkupWriter writer, IRequestCycle cycle)
 85   
     {
 86  5
         if (isInError(component))
 87   
         {
 88  1
             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  45
     protected FieldTracking getComponentTracking()
 107   
     {
 108  45
         return (FieldTracking) _trackingMap.get(_currentComponent.getName());
 109   
     }
 110   
 
 111  242
     public void setFormComponent(IFormComponent component)
 112   
     {
 113  242
         _currentComponent = component;
 114   
     }
 115   
 
 116  19
     public boolean isInError()
 117   
     {
 118  19
         IFieldTracking tracking = getComponentTracking();
 119   
 
 120  19
         return tracking != null && tracking.isInError();
 121   
     }
 122   
 
 123  1
     public String getFieldInputValue()
 124   
     {
 125  1
         IFieldTracking tracking = getComponentTracking();
 126   
 
 127  1
         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  9
     public void record(ValidatorException ex)
 166   
     {
 167  9
         IRender errorRenderer = ex.getErrorRenderer();
 168   
 
 169  9
         if (errorRenderer == null)
 170  8
             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  9
     public void record(String message, ValidationConstraint constraint)
 181   
     {
 182  9
         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  13
     public void recordFieldInputValue(String input)
 210   
     {
 211  13
         FieldTracking tracking = findCurrentTracking();
 212   
 
 213  13
         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  23
     protected FieldTracking findCurrentTracking()
 225   
     {
 226  23
         FieldTracking result = null;
 227   
 
 228  23
         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  22
             result = getComponentTracking();
 240   
 
 241  22
             if (result == null)
 242   
             {
 243  15
                 String fieldName = _currentComponent.getName();
 244   
 
 245  15
                 result = new FieldTracking(fieldName, _currentComponent);
 246   
 
 247  15
                 _trackings.add(result);
 248  15
                 _trackingMap.put(fieldName, result);
 249   
             }
 250   
         }
 251   
 
 252  23
         return result;
 253   
     }
 254   
 
 255   
     /**
 256   
      * Does nothing. Override in a subclass to decoreate fields.
 257   
      */
 258   
 
 259  7
     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  6
     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  6
     public void writeSuffix(IMarkupWriter writer, IRequestCycle cycle, IFormComponent component,
 279   
             IValidator validator)
 280   
     {
 281  6
         if (isInError())
 282   
         {
 283  1
             writer.printRaw("&nbsp;");
 284  1
             writer.begin("font");
 285  1
             writer.attribute("color", "red");
 286  1
             writer.print("**");
 287  1
             writer.end();
 288   
         }
 289   
     }
 290   
 
 291  10
     public boolean getHasErrors()
 292   
     {
 293  10
         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  17
     public IRender getFirstError()
 303   
     {
 304  17
         if (Tapestry.size(_trackings) == 0)
 305  5
             return null;
 306   
 
 307  12
         Iterator i = _trackings.iterator();
 308   
 
 309  12
         while (i.hasNext())
 310   
         {
 311  18
             IFieldTracking tracking = (IFieldTracking) i.next();
 312   
 
 313  18
             if (tracking.isInError())
 314  9
                 return tracking.getErrorRenderer();
 315   
         }
 316   
 
 317  3
         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  10
     protected boolean isInError(IFormComponent component)
 327   
     {
 328   
         // Get the name as most recently rendered.
 329   
 
 330  10
         String fieldName = component.getName();
 331   
 
 332  10
         IFieldTracking tracking = (IFieldTracking) _trackingMap.get(fieldName);
 333   
 
 334  10
         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   
 }