| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| AbstractTextField |
|
| 0.0;0 | ||||
| AbstractTextField$1 |
|
| 0.0;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, <input>). 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 | } |