Coverage Report - org.apache.tapestry5.corelib.base.AbstractTextField
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractTextField
100%
29/29
100%
8/8
0
AbstractTextField$1
100%
2/2
N/A
0
 
 1  
 // Copyright 2006, 2007, 2008, 2009 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.tapestry5.corelib.base;
 16  
 
 17  
 import org.apache.tapestry5.*;
 18  
 import org.apache.tapestry5.annotations.*;
 19  
 import org.apache.tapestry5.beaneditor.Width;
 20  
 import org.apache.tapestry5.corelib.mixins.RenderDisabled;
 21  
 import org.apache.tapestry5.ioc.AnnotationProvider;
 22  
 import org.apache.tapestry5.ioc.annotations.Inject;
 23  
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 24  
 import org.apache.tapestry5.services.ComponentDefaultProvider;
 25  
 import org.apache.tapestry5.services.Request;
 26  
 
 27  
 import java.lang.annotation.Annotation;
 28  
 import java.util.Locale;
 29  
 
 30  
 /**
 31  
  * Abstract class for a variety of components that render some variation of a text field. Most of the hooks for user
 32  
  * input validation are in this class.
 33  
  * <p/>
 34  
  * In particular, all subclasses support the "toclient" and "parseclient" events.  These two events allow the normal
 35  
  * {@link Translator} (specified by the translate parameter, but often automatically derived by Tapestry) to be
 36  
  * augmented.
 37  
  * <p/>
 38  
  * If the component container (i.e., the page) provides an event handler method for the "toclient" event, and that
 39  
  * handler returns a non-null string, that will be the string value sent to the client. The context passed to the event
 40  
  * handler method is t he current value of the value parameter.
 41  
  * <p/>
 42  
  * Likewise, on a form submit, the "parseclient" event handler method will be passed the string provided by the client,
 43  
  * and may provide a non-null value as the parsed value.  Returning null allows the normal translator to operate.  The
 44  
  * event handler may also throw {@link org.apache.tapestry5.ValidationException}.
 45  
  */
 46  
 @Events({ EventConstants.TO_CLIENT, EventConstants.VALIDATE, EventConstants.PARSE_CLIENT })
 47  497
 public abstract class AbstractTextField extends AbstractField
 48  
 {
 49  
     /**
 50  
      * The value to be read and updated. This is not necessarily a string, a translator may be provided to convert
 51  
      * between client side and server side representations. If not bound, a default binding is made to a property of the
 52  
      * container matching the component's id. If no such property exists, then you will see a runtime exception due to
 53  
      * the unbound value parameter.
 54  
      */
 55  
     @Parameter(required = true, principal = true)
 56  
     private Object value;
 57  
 
 58  
     /**
 59  
      * The object which will perform translation between server-side and client-side representations. If not specified,
 60  
      * a value will usually be generated based on the type of the value parameter.
 61  
      */
 62  
     @Parameter(required = true, allowNull = false, defaultPrefix = BindingConstants.TRANSLATE)
 63  
     private FieldTranslator<Object> translate;
 64  
 
 65  
     /**
 66  
      * The object that will perform input validation (which occurs after translation). The validate binding prefix is
 67  
      * generally used to provide this object in a declarative fashion.
 68  
      */
 69  
     @Parameter(defaultPrefix = BindingConstants.VALIDATE)
 70  
     @SuppressWarnings("unchecked")
 71  
     private FieldValidator<Object> validate;
 72  
 
 73  
     /**
 74  
      * Provider of annotations used for some defaults.  Annotation are usually provided in terms of the value parameter
 75  
      * (i.e., from the getter and/or setter bound to the value parameter).
 76  
      *
 77  
      * @see org.apache.tapestry5.beaneditor.Width
 78  
      */
 79  
     @Parameter
 80  
     private AnnotationProvider annotationProvider;
 81  
 
 82  
     /**
 83  
      * Defines how nulls on the server side, or sent from the client side, are treated. The selected strategy may
 84  
      * replace the nulls with some other value. The default strategy leaves nulls alone.  Another built-in strategy,
 85  
      * zero, replaces nulls with the value 0.
 86  
      */
 87  
     @Parameter(defaultPrefix = BindingConstants.NULLFIELDSTRATEGY, value = "default")
 88  
     private NullFieldStrategy nulls;
 89  
 
 90  
     @Environmental
 91  
     private ValidationTracker tracker;
 92  
 
 93  
     @Inject
 94  
     private ComponentResources resources;
 95  
 
 96  
     @Inject
 97  
     private Locale locale;
 98  
 
 99  
     @Inject
 100  
     private Request request;
 101  
 
 102  
     @Inject
 103  
     private FieldValidationSupport fieldValidationSupport;
 104  
 
 105  
     @SuppressWarnings("unused")
 106  
     @Mixin
 107  
     private RenderDisabled renderDisabled;
 108  
 
 109  
     @Inject
 110  
     private ComponentDefaultProvider defaultProvider;
 111  
 
 112  
     /**
 113  
      * Computes a default value for the "translate" parameter using {@link org.apache.tapestry5.services.ComponentDefaultProvider#defaultTranslator(String,
 114  
      * org.apache.tapestry5.ComponentResources)}.
 115  
      */
 116  
     final Binding defaultTranslate()
 117  
     {
 118  91
         return defaultProvider.defaultTranslatorBinding("value", resources);
 119  
     }
 120  
 
 121  
     final AnnotationProvider defaultAnnotationProvider()
 122  
     {
 123  91
         return new AnnotationProvider()
 124  
         {
 125  91
             public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
 126  
             {
 127  374
                 return resources.getParameterAnnotation("value", annotationClass);
 128  
             }
 129  
         };
 130  
     }
 131  
 
 132  
     /**
 133  
      * Computes a default value for the "validate" parameter using {@link org.apache.tapestry5.services.FieldValidatorDefaultSource}.
 134  
      */
 135  
     final Binding defaultValidate()
 136  
     {
 137  79
         return defaultProvider.defaultValidatorBinding("value", resources);
 138  
     }
 139  
 
 140  
     /**
 141  
      * The default value is a property of the container whose name matches the component's id. May return null if the
 142  
      * container does not have a matching property.
 143  
      *
 144  
      * @deprecated Likely to be removed in the future, use {@link org.apache.tapestry5.annotations.Parameter#autoconnect()}
 145  
      *             instead
 146  
      */
 147  
     final Binding defaultValue()
 148  
     {
 149  24
         return createDefaultParameterBinding("value");
 150  
     }
 151  
 
 152  
     @SuppressWarnings({ "unchecked" })
 153  
     @BeginRender
 154  
     void begin(MarkupWriter writer)
 155  
     {
 156  584
         String value = tracker.getInput(this);
 157  
 
 158  
         // If this is a response to a form submission, and the user provided a value.
 159  
         // then send that exact value back at them.
 160  
 
 161  584
         if (value == null)
 162  
         {
 163  
             // Otherwise, get the value from the parameter ...
 164  
             // Then let the translator and or various triggered events get it into
 165  
             // a format ready to be sent to the client.
 166  
 
 167  528
             value = fieldValidationSupport.toClient(this.value, resources, translate, nulls);
 168  
         }
 169  
 
 170  584
         writeFieldTag(writer, value);
 171  
 
 172  584
         translate.render(writer);
 173  584
         validate.render(writer);
 174  
 
 175  584
         resources.renderInformalParameters(writer);
 176  
 
 177  584
         decorateInsideField();
 178  584
     }
 179  
 
 180  
     /**
 181  
      * Invoked from {@link #begin(MarkupWriter)} to write out the element and attributes (typically, &lt;input&gt;). The
 182  
      * {@linkplain AbstractField#getControlName() controlName} and {@linkplain AbstractField#getClientId() clientId}
 183  
      * properties will already have been set or updated.
 184  
      * <p/>
 185  
      * Generally, the subclass will invoke {@link MarkupWriter#element(String, Object[])}, and will be responsible for
 186  
      * including an {@link AfterRender} phase method to invoke {@link MarkupWriter#end()}.
 187  
      *
 188  
      * @param writer markup write to send output to
 189  
      * @param value  the value (either obtained and translated from the value parameter, or obtained from the tracker)
 190  
      */
 191  
     protected abstract void writeFieldTag(MarkupWriter writer, String value);
 192  
 
 193  
     @SuppressWarnings({ "unchecked" })
 194  
     @Override
 195  
     protected void processSubmission(String elementName)
 196  
     {
 197  238
         String rawValue = request.getParameter(elementName);
 198  
 
 199  238
         tracker.recordInput(this, rawValue);
 200  
 
 201  
         try
 202  
         {
 203  238
             Object translated = fieldValidationSupport.parseClient(rawValue, resources, translate, nulls);
 204  
 
 205  234
             fieldValidationSupport.validate(translated, resources, validate);
 206  
 
 207  
             // If the value provided is blank and we're ignoring blank input (i.e. PasswordField),
 208  
             // then don't update the value parameter.
 209  
 
 210  196
             if (!(ignoreBlankInput() && InternalUtils.isBlank(rawValue)))
 211  194
                 value = translated;
 212  
         }
 213  42
         catch (ValidationException ex)
 214  
         {
 215  42
             tracker.recordError(this, ex.getMessage());
 216  196
         }
 217  238
     }
 218  
 
 219  
     /**
 220  
      * Should blank input be ignored (after validation)?  This will be true for {@link
 221  
      * org.apache.tapestry5.corelib.components.PasswordField}.
 222  
      */
 223  
     protected boolean ignoreBlankInput()
 224  
     {
 225  180
         return false;
 226  
     }
 227  
 
 228  
     @Override
 229  
     public boolean isRequired()
 230  
     {
 231  536
         return validate.isRequired();
 232  
     }
 233  
 
 234  
     /**
 235  
      * Looks for a {@link org.apache.tapestry5.beaneditor.Width} annotation and, if present, returns its value as a
 236  
      * string.
 237  
      *
 238  
      * @return the indicated width, or null if the annotation is not present
 239  
      */
 240  
     protected final String getWidth()
 241  
     {
 242  584
         Width width = annotationProvider.getAnnotation(Width.class);
 243  
 
 244  584
         if (width == null) return null;
 245  
 
 246  40
         return Integer.toString(width.value());
 247  
     }
 248  
 }