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