001: // Copyright 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.components;
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.OptionModel;
024: import org.apache.tapestry.SelectModel;
025: import org.apache.tapestry.SelectModelVisitor;
026: import org.apache.tapestry.ValidationException;
027: import org.apache.tapestry.ValidationTracker;
028: import org.apache.tapestry.ValueEncoder;
029: import org.apache.tapestry.annotations.BeforeRenderTemplate;
030: import org.apache.tapestry.annotations.Environmental;
031: import org.apache.tapestry.annotations.Inject;
032: import org.apache.tapestry.annotations.Parameter;
033: import org.apache.tapestry.corelib.base.AbstractField;
034: import org.apache.tapestry.internal.util.SelectModelRenderer;
035: import org.apache.tapestry.services.FieldValidatorDefaultSource;
036: import org.apache.tapestry.services.FormSupport;
037: import org.apache.tapestry.services.Request;
038: import org.apache.tapestry.services.ValueEncoderFactory;
039: import org.apache.tapestry.services.ValueEncoderSource;
040: import org.apache.tapestry.util.EnumSelectModel;
041:
042: /**
043: * Select an item from a list of values, using an [X]HTML <select> element on the client side.
044: * An validation decorations will go around the entire <select> element.
045: * <p>
046: * A core part of this component is the {@link ValueEncoder} (the encoder parameter) that is used to
047: * convert between server-side values and client-side strings. In many cases, a {@link ValueEncoder}
048: * can be generated automatically from the type of the value parameter. The
049: * {@link ValueEncoderSource} service provides an encoder in these situations; it can be overriden
050: * by binding the encoder parameter, or extended by contributing a {@link ValueEncoderFactory} into
051: * the service's configuration.
052: */
053: public final class Select extends AbstractField {
054: private class Renderer extends SelectModelRenderer {
055:
056: public Renderer(MarkupWriter writer) {
057: super (writer, _encoder);
058: }
059:
060: @Override
061: protected boolean isOptionSelected(OptionModel optionModel) {
062: Object value = optionModel.getValue();
063:
064: return value == _value
065: || (value != null && value.equals(_value));
066: }
067: }
068:
069: /**
070: * Allows a specific implementation of {@link ValueEncoder} to be supplied. This is used to
071: * create client-side string values for the different options.
072: *
073: * @see ValueEncoderSource
074: */
075: @Parameter
076: private ValueEncoder _encoder;
077:
078: @Inject
079: private FieldValidatorDefaultSource _fieldValidatorDefaultSource;
080:
081: @Inject
082: private Locale _locale;
083:
084: // Maybe this should default to property "<componentId>Model"?
085: /**
086: * The model used to identify the option groups and options to be presented to the user. This
087: * can be generated automatically for Enum types.
088: */
089: @Parameter(required=true)
090: private SelectModel _model;
091:
092: @Inject
093: private Request _request;
094:
095: @Inject
096: private ComponentResources _resources;
097:
098: @Environmental
099: private ValidationTracker _tracker;
100:
101: /** Performs input validation on the value supplied by the user in the form submission. */
102: @Parameter(defaultPrefix="validate")
103: @SuppressWarnings("unchecked")
104: private FieldValidator<Object> _validate = NOOP_VALIDATOR;
105:
106: /** The value to read or update. */
107: @Parameter(required=true,principal=true)
108: private Object _value;
109:
110: @Inject
111: private ValueEncoderSource _valueEncoderSource;
112:
113: @Override
114: protected void processSubmission(FormSupport formSupport,
115: String elementName) {
116: String primaryKey = _request.getParameter(elementName);
117:
118: Object selectedValue = _encoder.toValue(primaryKey);
119:
120: try {
121: _validate.validate(selectedValue);
122:
123: _value = selectedValue;
124: } catch (ValidationException ex) {
125: _tracker.recordError(this , ex.getMessage());
126: return;
127: }
128: }
129:
130: void afterRender(MarkupWriter writer) {
131: writer.end();
132: }
133:
134: void beginRender(MarkupWriter writer) {
135: writer.element("select", "name", getElementName(), "id",
136: getClientId());
137:
138: // Disabled, informals via mixins
139: }
140:
141: @SuppressWarnings("unchecked")
142: ValueEncoder defaultEncoder() {
143: return _valueEncoderSource.createEncoder("value", _resources);
144: }
145:
146: @SuppressWarnings("unchecked")
147: SelectModel defaultModel() {
148: Class valueType = _resources.getBoundType("value");
149:
150: if (valueType == null)
151: return null;
152:
153: if (Enum.class.isAssignableFrom(valueType))
154: return new EnumSelectModel(valueType, _resources
155: .getContainerMessages());
156:
157: return null;
158: }
159:
160: /**
161: * Computes a default value for the "validate" parameter using
162: * {@link FieldValidatorDefaultSource}.
163: */
164: FieldValidator defaultValidate() {
165: Class type = _resources.getBoundType("value");
166:
167: if (type == null)
168: return null;
169:
170: return _fieldValidatorDefaultSource.createDefaultValidator(
171: this , _resources.getId(), _resources
172: .getContainerMessages(), _locale, type,
173: _resources.getAnnotationProvider("value"));
174: }
175:
176: Binding defaultValue() {
177: return createDefaultParameterBinding("value");
178: }
179:
180: @BeforeRenderTemplate
181: void options(MarkupWriter writer) {
182: SelectModelVisitor renderer = new Renderer(writer);
183:
184: _model.visit(renderer);
185: }
186:
187: // For testing.
188:
189: void setModel(SelectModel model) {
190: _model = model;
191: }
192:
193: void setValue(Object value) {
194: _value = value;
195: }
196:
197: void setValueEncoder(ValueEncoder encoder) {
198: _encoder = encoder;
199: }
200: }
|