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