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