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.io.Serializable;
018:
019: import org.apache.tapestry.Binding;
020: import org.apache.tapestry.ComponentAction;
021: import org.apache.tapestry.ComponentResources;
022: import org.apache.tapestry.Field;
023: import org.apache.tapestry.FieldValidator;
024: import org.apache.tapestry.MarkupWriter;
025: import org.apache.tapestry.PageRenderSupport;
026: import org.apache.tapestry.ValidationDecorator;
027: import org.apache.tapestry.ValidationException;
028: import org.apache.tapestry.annotations.AfterRender;
029: import org.apache.tapestry.annotations.BeginRender;
030: import org.apache.tapestry.annotations.Environmental;
031: import org.apache.tapestry.annotations.Inject;
032: import org.apache.tapestry.annotations.Mixin;
033: import org.apache.tapestry.annotations.Parameter;
034: import org.apache.tapestry.annotations.SetupRender;
035: import org.apache.tapestry.corelib.mixins.DiscardBody;
036: import org.apache.tapestry.corelib.mixins.RenderDisabled;
037: import org.apache.tapestry.corelib.mixins.RenderInformals;
038: import org.apache.tapestry.services.ComponentDefaultProvider;
039: import org.apache.tapestry.services.FormSupport;
040:
041: /**
042: * Provides initialization of the clientId and elementName properties. In addition, adds the
043: * {@link RenderInformals}, {@link RenderDisabled} and {@link DiscardBody} mixins.
044: */
045: public abstract class AbstractField implements Field {
046: /**
047: * The user presentable label for the field. If not provided, a reasonable label is generated
048: * from the component's id, first by looking for a message key named "id-label" (substituting
049: * the component's actual id), then by converting the actual id to a presentable string (for
050: * example, "userId" to "User Id").
051: */
052: @Parameter(defaultPrefix="literal")
053: private String _label;
054:
055: /**
056: * If true, then the field will render out with a disabled attribute (to turn off client-side
057: * behavior). Further, a disabled field ignores any value in the request when the form is
058: * submitted.
059: */
060: @Parameter("false")
061: private boolean _disabled;
062:
063: @SuppressWarnings("unused")
064: @Mixin
065: private RenderInformals _renderInformals;
066:
067: @SuppressWarnings("unused")
068: @Mixin
069: private RenderDisabled _renderDisabled;
070:
071: @SuppressWarnings("unused")
072: @Mixin
073: private DiscardBody _discardBody;
074:
075: @Environmental
076: private ValidationDecorator _decorator;
077:
078: protected static final FieldValidator NOOP_VALIDATOR = new FieldValidator() {
079: public void validate(Object value) throws ValidationException {
080: // Do nothing
081: }
082:
083: public void render(MarkupWriter writer) {
084: }
085: };
086:
087: static class SetupAction implements ComponentAction<AbstractField>,
088: Serializable {
089: private static final long serialVersionUID = 2690270808212097020L;
090:
091: private final String _elementName;
092:
093: public SetupAction(final String elementName) {
094: _elementName = elementName;
095: }
096:
097: public void execute(AbstractField component) {
098: component.setupElementName(_elementName);
099: }
100: }
101:
102: static class ProcessSubmissionAction implements
103: ComponentAction<AbstractField>, Serializable {
104: private static final long serialVersionUID = -4346426414137434418L;
105:
106: public void execute(AbstractField component) {
107: component.processSubmission();
108: }
109: }
110:
111: /** Used a shared instance for all types of fields, for efficiency. */
112: private static final ProcessSubmissionAction PROCESS_SUBMISSION_ACTION = new ProcessSubmissionAction();
113:
114: /**
115: * The id used to generate a page-unique client-side identifier for the component. If a
116: * component renders multiple times, a suffix will be appended to the to id to ensure
117: * uniqueness. The uniqued value may be accessed via the
118: * {@link #getClientId() clientId property}.
119: */
120: @Parameter(value="prop:componentResources.id",defaultPrefix="literal")
121: private String _clientId;
122:
123: private String _assignedClientId;
124:
125: private String _elementName;
126:
127: @Environmental
128: private FormSupport _formSupport;
129:
130: @Environmental
131: private PageRenderSupport _pageRenderSupport;
132:
133: @Inject
134: private ComponentResources _resources;
135:
136: @Inject
137: private ComponentDefaultProvider _defaultProvider;
138:
139: final String defaultLabel() {
140: return _defaultProvider.defaultLabel(_resources);
141: }
142:
143: public final String getLabel() {
144: return _label;
145: }
146:
147: @SetupRender
148: final void setup() {
149: // By default, use the component id as the (base) client id. If the clientid
150: // parameter is bound, then that is the value to use.
151:
152: String id = _clientId;
153:
154: // Often, these elementName and _clientId will end up as the same value. There are many
155: // exceptions, including a form that renders inside a loop, or a form inside a component
156: // that is used multiple times.
157:
158: _assignedClientId = _pageRenderSupport.allocateClientId(id);
159: String elementName = _formSupport.allocateElementName(id);
160:
161: _formSupport
162: .storeAndExecute(this , new SetupAction(elementName));
163: _formSupport.store(this , PROCESS_SUBMISSION_ACTION);
164: }
165:
166: public final String getClientId() {
167: return _assignedClientId;
168: }
169:
170: public final String getElementName() {
171: return _elementName;
172: }
173:
174: public final boolean isDisabled() {
175: return _disabled;
176: }
177:
178: /**
179: * Invoked from within a ComponentCommand callback, to restore the component's elementName.
180: */
181: private void setupElementName(String elementName) {
182: _elementName = elementName;
183: }
184:
185: private void processSubmission() {
186: if (!_disabled)
187: processSubmission(_formSupport, _elementName);
188: }
189:
190: /**
191: * Used by subclasses to create a default binding to a property of the container matching the
192: * component id.
193: *
194: * @return a binding to the property, or null if the container does not have a corresponding
195: * property
196: */
197: protected final Binding createDefaultParameterBinding(
198: String parameterName) {
199: return _defaultProvider.defaultBinding(parameterName,
200: _resources);
201: }
202:
203: /**
204: * Method implemented by subclasses to actually do the work of processing the submission of the
205: * form. The element's elementName property will already have been set. This method is only
206: * invoked if the field is <strong>not {@link #isDisabled() disabled}</strong>.
207: *
208: * @param formSupport
209: * support for the form submission, used to
210: * {@link FormSupport#getParameterValue(String) obtain submitted parameter values}.
211: * Passing this value in saves subclasses from having to (re)inject it.
212: * @param elementName
213: * the name of the element (used to find the correct parameter in the request)
214: */
215: protected abstract void processSubmission(FormSupport formSupport,
216: String elementName);
217:
218: @BeginRender
219: final void beforeDecorator(MarkupWriter writer) {
220: _decorator.beforeField(this );
221: }
222:
223: @AfterRender
224: final void afterDecorator(MarkupWriter writer) {
225: _decorator.afterField(this );
226: }
227:
228: protected final ValidationDecorator getValidationDecorator() {
229: return _decorator;
230: }
231: }
|