Coverage Report - org.apache.tapestry5.corelib.base.AbstractField
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractField
87%
27/31
75%
3/4
0
AbstractField$ProcessSubmission
75%
3/4
N/A
0
AbstractField$Setup
86%
6/7
N/A
0
 
 1  
 // Copyright 2006, 2007, 2008 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.corelib.internal.InternalMessages;
 20  
 import org.apache.tapestry5.corelib.mixins.DiscardBody;
 21  
 import org.apache.tapestry5.corelib.mixins.RenderDisabled;
 22  
 import org.apache.tapestry5.corelib.mixins.RenderInformals;
 23  
 import org.apache.tapestry5.ioc.annotations.Inject;
 24  
 import org.apache.tapestry5.services.ComponentDefaultProvider;
 25  
 import org.apache.tapestry5.services.FormSupport;
 26  
 
 27  
 import java.io.Serializable;
 28  
 
 29  
 /**
 30  
  * Provides initialization of the clientId and elementName properties. In addition, adds the {@link RenderInformals},
 31  
  * {@link RenderDisabled} and {@link DiscardBody} mixins.
 32  
  */
 33  
 @SupportsInformalParameters
 34  1734
 public abstract class AbstractField implements Field
 35  
 {
 36  
     /**
 37  
      * The user presentable label for the field. If not provided, a reasonable label is generated from the component's
 38  
      * id, first by looking for a message key named "id-label" (substituting the component's actual id), then by
 39  
      * converting the actual id to a presentable string (for example, "userId" to "User Id").
 40  
      */
 41  
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
 42  
     private String label;
 43  
 
 44  
     /**
 45  
      * If true, then the field will render out with a disabled attribute (to turn off client-side behavior). Further, a
 46  
      * disabled field ignores any value in the request when the form is submitted.
 47  
      */
 48  
     @Parameter("false")
 49  
     private boolean disabled;
 50  
 
 51  
     @SuppressWarnings("unused")
 52  
     @Mixin
 53  
     private DiscardBody discardBody;
 54  
 
 55  
     @Environmental
 56  
     private ValidationDecorator decorator;
 57  
 
 58  
 
 59  1180
     static class Setup implements ComponentAction<AbstractField>, Serializable
 60  
     {
 61  
         private static final long serialVersionUID = 2690270808212097020L;
 62  
 
 63  
         private final String controlName;
 64  
 
 65  
         public Setup(String controlName)
 66  842
         {
 67  842
             this.controlName = controlName;
 68  842
         }
 69  
 
 70  
         public void execute(AbstractField component)
 71  
         {
 72  1180
             component.setupControlName(controlName);
 73  1180
         }
 74  
 
 75  
         @Override
 76  
         public String toString()
 77  
         {
 78  0
             return String.format("AbstractField.Setup[%s]", controlName);
 79  
         }
 80  
     }
 81  
 
 82  216
     static class ProcessSubmission implements ComponentAction<AbstractField>, Serializable
 83  
     {
 84  
         private static final long serialVersionUID = -4346426414137434418L;
 85  
 
 86  
         public void execute(AbstractField component)
 87  
         {
 88  338
             component.processSubmission();
 89  338
         }
 90  
 
 91  
         @Override
 92  
         public String toString()
 93  
         {
 94  0
             return "AbstractField.ProcessSubmission";
 95  
         }
 96  
     }
 97  
 
 98  
     /**
 99  
      * Used a shared instance for all types of fields, for efficiency.
 100  
      */
 101  18
     private static final ProcessSubmission PROCESS_SUBMISSION_ACTION = new ProcessSubmission();
 102  
 
 103  
     /**
 104  
      * The id used to generate a page-unique client-side identifier for the component. If a component renders multiple
 105  
      * times, a suffix will be appended to the to id to ensure uniqueness. The uniqued value may be accessed via the
 106  
      * {@link #getClientId() clientId property}.
 107  
      */
 108  
     @Parameter(value = "prop:componentResources.id", defaultPrefix = BindingConstants.LITERAL)
 109  
     private String clientId;
 110  
 
 111  
     private String assignedClientId;
 112  
 
 113  
     private String controlName;
 114  
 
 115  
     @Environmental(false)
 116  
     private FormSupport formSupport;
 117  
 
 118  
     @Environmental
 119  
     private RenderSupport renderSupport;
 120  
 
 121  
     @Inject
 122  
     private ComponentResources resources;
 123  
 
 124  
     @Inject
 125  
     private ComponentDefaultProvider defaultProvider;
 126  
 
 127  
     final String defaultLabel()
 128  
     {
 129  135
         return defaultProvider.defaultLabel(resources);
 130  
     }
 131  
 
 132  
     public final String getLabel()
 133  
     {
 134  1950
         return label;
 135  
     }
 136  
 
 137  
     @SetupRender
 138  
     final void setup()
 139  
     {
 140  
         // By default, use the component id as the (base) client id. If the clientid
 141  
         // parameter is bound, then that is the value to use.
 142  
 
 143  844
         String id = clientId;
 144  
 
 145  
         // Often, these controlName and clientId will end up as the same value. There are many
 146  
         // exceptions, including a form that renders inside a loop, or a form inside a component
 147  
         // that is used multiple times.
 148  
 
 149  844
         if (formSupport == null) throw new RuntimeException(InternalMessages.formFieldOutsideForm(getLabel()));
 150  
 
 151  842
         assignedClientId = renderSupport.allocateClientId(id);
 152  842
         String controlName = formSupport.allocateControlName(id);
 153  
 
 154  842
         formSupport.storeAndExecute(this, new Setup(controlName));
 155  842
         formSupport.store(this, PROCESS_SUBMISSION_ACTION);
 156  842
     }
 157  
 
 158  
     public final String getClientId()
 159  
     {
 160  3312
         return assignedClientId;
 161  
     }
 162  
 
 163  
     public final String getControlName()
 164  
     {
 165  4754
         return controlName;
 166  
     }
 167  
 
 168  
     public final boolean isDisabled()
 169  
     {
 170  1546
         return disabled;
 171  
     }
 172  
 
 173  
     /**
 174  
      * Invoked from within a ComponentCommand callback, to restore the component's elementName.
 175  
      */
 176  
     private void setupControlName(String controlName)
 177  
     {
 178  1180
         this.controlName = controlName;
 179  1180
     }
 180  
 
 181  
     private void processSubmission()
 182  
     {
 183  338
         if (!disabled) processSubmission(controlName);
 184  338
     }
 185  
 
 186  
     /**
 187  
      * Used by subclasses to create a default binding to a property of the container matching the component id.
 188  
      *
 189  
      * @return a binding to the property, or null if the container does not have a corresponding property
 190  
      */
 191  
     protected final Binding createDefaultParameterBinding(String parameterName)
 192  
     {
 193  24
         return defaultProvider.defaultBinding(parameterName, resources);
 194  
     }
 195  
 
 196  
     /**
 197  
      * Method implemented by subclasses to actually do the work of processing the submission of the form. The element's
 198  
      * elementName property will already have been set. This method is only invoked if the field is <strong>not {@link
 199  
      * #isDisabled() disabled}</strong>.
 200  
      *
 201  
      * @param elementName the name of the element (used to find the correct parameter in the request)
 202  
      */
 203  
     protected abstract void processSubmission(String elementName);
 204  
 
 205  
     /**
 206  
      * Allows the validation decorator to write markup before the field itself writes markup.
 207  
      */
 208  
     @BeginRender
 209  
     final void beforeDecorator()
 210  
     {
 211  842
         decorator.beforeField(this);
 212  842
     }
 213  
 
 214  
     /**
 215  
      * Allows the validation decorator to write markup after the field has written all of its markup.
 216  
      */
 217  
     @AfterRender
 218  
     final void afterDecorator()
 219  
     {
 220  842
         decorator.afterField(this);
 221  842
     }
 222  
 
 223  
     /**
 224  
      * Invoked from subclasses after they have written their tag and (where appropriate) their informal parameters
 225  
      * <em>and</em> have allowed their {@link Validator} to write markup as well.
 226  
      */
 227  
     protected final void decorateInsideField()
 228  
     {
 229  686
         decorator.insideField(this);
 230  686
     }
 231  
 
 232  
     protected final void setDecorator(ValidationDecorator decorator)
 233  
     {
 234  0
         this.decorator = decorator;
 235  0
     }
 236  
 
 237  
     protected final void setFormSupport(FormSupport formSupport)
 238  
     {
 239  0
         this.formSupport = formSupport;
 240  0
     }
 241  
 
 242  
     /**
 243  
      * Returns false; most components do not support declarative validation.
 244  
      */
 245  
     public boolean isRequired()
 246  
     {
 247  70
         return false;
 248  
     }
 249  
 }