001 /*
002 * Apache License
003 * Version 2.0, January 2004
004 * http://www.apache.org/licenses/
005 *
006 * Copyright 2008-2010 by chenillekit.org
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 */
014
015 package org.chenillekit.tapestry.core.components;
016
017 import java.util.List;
018 import java.util.Locale;
019
020 import org.apache.tapestry5.Binding;
021 import org.apache.tapestry5.BindingConstants;
022 import org.apache.tapestry5.ComponentResources;
023 import org.apache.tapestry5.FieldValidator;
024 import org.apache.tapestry5.MarkupWriter;
025 import org.apache.tapestry5.OptionModel;
026 import org.apache.tapestry5.SelectModel;
027 import org.apache.tapestry5.SelectModelVisitor;
028 import org.apache.tapestry5.ValidationException;
029 import org.apache.tapestry5.ValidationTracker;
030 import org.apache.tapestry5.ValueEncoder;
031 import org.apache.tapestry5.annotations.BeforeRenderTemplate;
032 import org.apache.tapestry5.annotations.Environmental;
033 import org.apache.tapestry5.annotations.Parameter;
034 import org.apache.tapestry5.corelib.base.AbstractField;
035 import org.apache.tapestry5.dom.Element;
036 import org.apache.tapestry5.ioc.annotations.Inject;
037 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
038 import org.apache.tapestry5.ioc.internal.util.TapestryException;
039 import org.apache.tapestry5.services.ComponentDefaultProvider;
040 import org.apache.tapestry5.services.Request;
041 import org.apache.tapestry5.util.EnumSelectModel;
042
043 import org.chenillekit.tapestry.core.encoders.MultipleValueEncoder;
044 import org.chenillekit.tapestry.core.renderes.MultipleSelectModelRenderer;
045
046
047 /**
048 * Select a list of items from a list of values, using an [X]HTML multiple select element on the client side.
049 * <p/>
050 * the only diffrence to the original tapestry select component is that the "value" parameter expected a java.util.List object.
051 *
052 * @version $Id: MultipleSelect.java 729 2010-11-03 19:51:08Z homburgs $
053 * @link http://tapestry.apache.org/t5components/tapestry-core/component-parameters.html#orgapachetapestrycorelibcomponentsselect
054 */
055 public class MultipleSelect extends AbstractField
056 {
057 private class Renderer extends MultipleSelectModelRenderer
058 {
059 public Renderer(MarkupWriter writer)
060 {
061 super(writer, encoder);
062 }
063
064 @Override
065 protected boolean isOptionSelected(OptionModel optionModel)
066 {
067 boolean hit = false;
068 Object testValue = optionModel.getValue();
069
070 if (value != null)
071 {
072 for (Object singleValue : value)
073 {
074 hit = testValue == singleValue || (testValue != null && testValue.equals(singleValue));
075 if (hit)
076 break;
077 }
078 }
079
080 return hit;
081 }
082 }
083
084 /**
085 * Allows a specific implementation of org.apache.tapestry5.ValueEncoder to be supplied.
086 * This is used to create client-side string values for the different options.
087 */
088 @Parameter
089 private MultipleValueEncoder encoder;
090
091 /**
092 * The model used to identify the option groups and options to be presented to the user.
093 * This can be generated automatically for Enum types.
094 */
095 @Parameter(required = true)
096 private SelectModel model;
097
098 /**
099 * should the component multi select able.
100 */
101 @Parameter(defaultPrefix = BindingConstants.PROP, value = "true")
102 @SuppressWarnings("unchecked")
103 private boolean multiple;
104
105 @Parameter(defaultPrefix = BindingConstants.VALIDATE)
106 @SuppressWarnings("unchecked")
107 private FieldValidator<Object> validate;
108
109 /**
110 * The list of value to read or update.
111 */
112 @Parameter(required = true, principal = true)
113 private List value;
114
115 @Inject
116 private Locale locale;
117
118 @Inject
119 private Request request;
120
121 @Inject
122 private ComponentResources resources;
123
124 @Environmental
125 private ValidationTracker tracker;
126
127 @Inject
128 private ComponentDefaultProvider defaultProvider;
129
130 @Override
131 protected void processSubmission(String elementName)
132 {
133 String[] primaryKeys = request.getParameters(elementName);
134 List selectedValues = primaryKeys != null ? encoder.toValue(primaryKeys) : CollectionFactory.newList();
135
136 try
137 {
138 for (Object selectedValue : selectedValues)
139 validate.validate(selectedValue);
140
141 if (validate.isRequired() && selectedValues.size() == 0)
142 throw new ValidationException("at least one selection is required");
143
144 value = selectedValues;
145 }
146 catch (ValidationException ex)
147 {
148 tracker.recordError(this, ex.getMessage());
149 }
150 }
151
152 void afterRender(MarkupWriter writer)
153 {
154 writer.end();
155 }
156
157 void beginRender(MarkupWriter writer)
158 {
159 Element element = writer.element("select", "name", getControlName(), "id", getClientId());
160
161 if (multiple)
162 element.attribute("multiple", "multiple");
163
164 resources.renderInformalParameters(writer);
165 }
166
167 @SuppressWarnings("unchecked")
168 ValueEncoder defaultEncoder()
169 {
170 return defaultProvider.defaultValueEncoder("value", resources);
171 }
172
173 @SuppressWarnings("unchecked")
174 SelectModel defaultModel()
175 {
176 Class valueType = resources.getBoundType("value");
177
178 if (valueType == null) return null;
179
180 if (Enum.class.isAssignableFrom(valueType))
181 return new EnumSelectModel(valueType, resources.getContainerMessages());
182
183 return null;
184 }
185
186 Binding defaultValidate()
187 {
188 return defaultProvider.defaultValidatorBinding("value", resources);
189 }
190
191 Binding defaultValue()
192 {
193 return defaultProvider.defaultBinding("value", resources);
194 }
195
196 @BeforeRenderTemplate
197 void options(MarkupWriter writer)
198 {
199 SelectModelVisitor renderer = new Renderer(writer);
200 if (model == null)
201 throw new TapestryException("select model cannot be null", this, null);
202
203 model.visit(renderer);
204 }
205
206 /**
207 * only for testing.
208 *
209 * @param model
210 */
211 public void setModel(SelectModel model)
212 {
213 this.model = model;
214 }
215
216 /**
217 * only for testing.
218 *
219 * @param value
220 */
221 public void setValue(List value)
222 {
223 this.value = value;
224 }
225
226 /**
227 * only for testing.
228 *
229 * @param encoder
230 */
231 public void setValueEncoder(MultipleValueEncoder encoder)
232 {
233 this.encoder = encoder;
234 }
235 }