001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.corelib.base;
016:
017: import java.util.Locale;
018:
019: import org.apache.tapestry.Binding;
020: import org.apache.tapestry.ComponentResources;
021: import org.apache.tapestry.FieldValidator;
022: import org.apache.tapestry.MarkupWriter;
023: import org.apache.tapestry.Translator;
024: import org.apache.tapestry.ValidationException;
025: import org.apache.tapestry.ValidationTracker;
026: import org.apache.tapestry.annotations.AfterRender;
027: import org.apache.tapestry.annotations.BeginRender;
028: import org.apache.tapestry.annotations.Environmental;
029: import org.apache.tapestry.annotations.Inject;
030: import org.apache.tapestry.annotations.Parameter;
031: import org.apache.tapestry.ioc.Messages;
032: import org.apache.tapestry.services.FieldValidatorDefaultSource;
033: import org.apache.tapestry.services.FormSupport;
034: import org.apache.tapestry.services.Request;
035: import org.apache.tapestry.services.TranslatorDefaultSource;
036: import org.apache.tapestry.services.ValidationMessagesSource;
037:
038: /**
039: * Abstract class for a variety of components that render some variation of a text field. Most of
040: * the hooks for user input validation are in this class.
041: */
042: public abstract class AbstractTextField extends AbstractField {
043: /**
044: * The value to be read and updated. This is not necessarilly a string, a translator may be
045: * provided to convert between client side and server side representations. If not bound, a
046: * default binding is made to a property of the container matching the component's id. If no
047: * such property exists, then you will see a runtime exception due to the unbound value
048: * parameter.
049: */
050: @Parameter(required=true,principal=true)
051: private Object _value;
052:
053: /**
054: * The object which will perform translation between server-side and client-side
055: * representations. If not specified, a value will usually be generated based on the type of the
056: * value parameter.
057: */
058: @Parameter(required=true)
059: private Translator<Object> _translate;
060:
061: /**
062: * The object that will peform input validation (which occurs after translation). The translate
063: * binding prefix is genereally used to provide this object in a declarative fashion.
064: */
065: @Parameter(defaultPrefix="validate")
066: @SuppressWarnings("unchecked")
067: private FieldValidator<Object> _validate = NOOP_VALIDATOR;
068:
069: @Environmental
070: private ValidationTracker _tracker;
071:
072: @Inject
073: private ValidationMessagesSource _messagesSource;
074:
075: @Inject
076: private TranslatorDefaultSource _translatorDefaultSource;
077:
078: @Inject
079: private FieldValidatorDefaultSource _fieldValidatorDefaultSource;
080:
081: @Inject
082: private ComponentResources _resources;
083:
084: @Inject
085: private Locale _locale;
086:
087: @Inject
088: private Request _request;
089:
090: /**
091: * Computes a default value for the "translate" parameter using {@link TranslatorDefaultSource}.
092: */
093: final Translator defaultTranslate() {
094: // Because the value parameter is a principal parameter, we know that it will be bound (even
095: // via its default parameter) by the time this method is invoked.
096:
097: Class type = _resources.getBoundType("value");
098:
099: if (type == null)
100: return null;
101:
102: return _translatorDefaultSource.find(type);
103: }
104:
105: /**
106: * Computes a default value for the "validate" parameter using
107: * {@link FieldValidatorDefaultSource}.
108: */
109: final FieldValidator defaultValidate() {
110: Class type = _resources.getBoundType("value");
111:
112: if (type == null)
113: return null;
114:
115: return _fieldValidatorDefaultSource.createDefaultValidator(
116: this , _resources.getId(), _resources
117: .getContainerMessages(), _locale, type,
118: _resources.getAnnotationProvider("value"));
119: }
120:
121: /**
122: * The default value is a property of the container whose name matches the component's id. May
123: * return null if the container does not have a matching property.
124: */
125: final Binding defaultValue() {
126: return createDefaultParameterBinding("value");
127: }
128:
129: @BeginRender
130: final void begin(MarkupWriter writer) {
131: String value = _tracker.getInput(this );
132:
133: if (value == null)
134: value = _translate.toClient(_value);
135:
136: writeFieldTag(writer, value);
137:
138: _validate.render(writer);
139:
140: getValidationDecorator().insideField(this );
141: }
142:
143: /**
144: * Invoked from {@link #begin(MarkupWriter)} to write out the element and attributes (typically,
145: * <input>). The {@link AbstractField#getElementName() elementName} and
146: * {@link AbstractField#getClientId() clientId} properties will already have been set or
147: * updated.
148: * <p>
149: * Generally, the subclass will invoke {@link MarkupWriter#element(String, Object[])}, and will
150: * be responsible for including an {@link AfterRender} phase method to invoke
151: * {@link MarkupWriter#end()}.
152: *
153: * @param writer
154: * markup write to send output to
155: * @param value
156: * the value (either obtained and translated from the value parameter, or obtained
157: * from the tracker)
158: */
159: protected abstract void writeFieldTag(MarkupWriter writer,
160: String value);
161:
162: @Override
163: protected final void processSubmission(FormSupport formSupport,
164: String elementName) {
165: String rawValue = _request.getParameter(elementName);
166:
167: _tracker.recordInput(this , rawValue);
168:
169: Messages messages = _messagesSource
170: .getValidationMessages(_locale);
171:
172: try {
173: Object translated = _translate.parseClient(rawValue,
174: messages);
175:
176: _validate.validate(translated);
177:
178: _value = translated;
179: } catch (ValidationException ex) {
180: _tracker.recordError(this, ex.getMessage());
181: return;
182: }
183: }
184: }
|