001 // Copyright 2004, 2005 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.contrib.form;
016
017 import java.util.ArrayList;
018 import java.util.Collection;
019 import java.util.List;
020
021 import org.apache.tapestry.IMarkupWriter;
022 import org.apache.tapestry.IRequestCycle;
023 import org.apache.tapestry.Tapestry;
024 import org.apache.tapestry.form.AbstractFormComponent;
025 import org.apache.tapestry.form.IPropertySelectionModel;
026 import org.apache.tapestry.form.ValidatableField;
027 import org.apache.tapestry.form.ValidatableFieldSupport;
028 import org.apache.tapestry.valid.ValidatorException;
029
030 /**
031 * A component which uses <input type=checkbox> to set a property of some
032 * object. Typically, the values for the object are defined using an
033 * {@link java.lang.Enum}. A MultiplePropertySelection is
034 * dependent on an {link IPropertySelectionModel} to provide the list of
035 * possible values.
036 * <p>
037 * Often, this is used to select one or more
038 * {@link java.lang.Enum} to assign to a property; the
039 * {@link org.apache.tapestry.form.EnumPropertySelectionModel}class simplifies
040 * this.
041 * <p>
042 * The {@link org.apache.tapestry.contrib.palette.Palette}component is more
043 * powerful, but requires client-side JavaScript and is not fully cross-browser
044 * compatible.
045 * <p>
046 * <table border=1>
047 * <tr>
048 * <td>Parameter</td>
049 * <td>Type</td>
050 * <td>Direction</td>
051 * <td>Required</td>
052 * <td>Default</td>
053 * <td>Description</td>
054 * </tr>
055 * <tr>
056 * <td>selectedList</td>
057 * <td>java.util.List</td>
058 * <td>in-out</td>
059 * <td>yes</td>
060 * <td> </td>
061 * <td>The property to set. During rendering, this property is read, and sets
062 * the default value of the options in the select. When the form is submitted,
063 * list is cleared, then has each selected option added to it.</td>
064 * </tr>
065 * <tr>
066 * <td>renderer</td>
067 * <td>{@link IMultiplePropertySelectionRenderer}</td>
068 * <td>in</td>
069 * <td>no</td>
070 * <td>shared instance of {@link CheckBoxMultiplePropertySelectionRenderer}</td>
071 * <td>Defines the object used to render this component. The default renders a
072 * table of checkboxes. </td>
073 * </tr>
074 * <tr>
075 * <td>model</td>
076 * <td>{@link IPropertySelectionModel}</td>
077 * <td>in</td>
078 * <td>yes</td>
079 * <td> </td>
080 * <td>The model provides a list of possible labels, and matches those labels
081 * against possible values that can be assigned back to the property.</td>
082 * </tr>
083 * <tr>
084 * <td>disabled</td>
085 * <td>boolean</td>
086 * <td>in</td>
087 * <td>no</td>
088 * <td>false</td>
089 * <td>Controls whether the <select> is active or not. A disabled
090 * PropertySelection does not update its value parameter.
091 * <p>
092 * Corresponds to the <code>disabled</code> HTML attribute.</td>
093 * </tr>
094 * </table>
095 * <p>
096 * Informal parameters are not allowed.
097 * <p>
098 * As of 4.0, this component can be validated.
099 *
100 * @author Sanjay Munjal
101 */
102
103 public abstract class MultiplePropertySelection extends AbstractFormComponent
104 implements ValidatableField
105 {
106
107 /**
108 * A shared instance of {@link CheckBoxMultiplePropertySelectionRenderer}.
109 */
110 public static final IMultiplePropertySelectionRenderer DEFAULT_CHECKBOX_RENDERER = new CheckBoxMultiplePropertySelectionRenderer();
111
112 public abstract Collection getSelectedList();
113
114 public abstract void setSelectedList(Collection selectedList);
115
116 protected void finishLoad()
117 {
118 setRenderer(DEFAULT_CHECKBOX_RENDERER);
119 }
120
121 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle)
122 {
123 Collection selectedList = getSelectedList();
124
125 if (selectedList == null)
126 throw Tapestry.createRequiredParameterException(this,
127 "selectedList");
128
129 IPropertySelectionModel model = getModel();
130
131 if (model == null)
132 throw Tapestry.createRequiredParameterException(this, "model");
133
134 IMultiplePropertySelectionRenderer renderer = getRenderer();
135
136 // Start rendering
137 renderer.beginRender(this, writer, cycle);
138
139 int count = model.getOptionCount();
140
141 for(int i = 0; i < count; i++)
142 {
143 Object option = model.getOption(i);
144
145 // Try to find the option in the list and if yes, then it is
146 // checked.
147 boolean optionSelected = selectedList.contains(option);
148
149 renderer.renderOption(this, writer, cycle, model, option, i,
150 optionSelected);
151 }
152
153 // A PropertySelection doesn't allow a body, so no need to worry about
154 // wrapped components.
155 renderer.endRender(this, writer, cycle);
156 }
157
158 /**
159 * @see org.apache.tapestry.form.AbstractRequirableField#rewindFormComponent(org.apache.tapestry.IMarkupWriter,
160 * org.apache.tapestry.IRequestCycle)
161 */
162 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
163 {
164 // get all the values
165 String[] optionValues = cycle.getParameters(getName());
166
167 IPropertySelectionModel model = getModel();
168
169 List selectedList = new ArrayList(getModel().getOptionCount());
170
171 // Nothing was selected
172 if (optionValues != null)
173 {
174 // Go through the array and translate and put back in the list
175 for(int i = 0; i < optionValues.length; i++)
176 {
177 // Translate the new value
178 Object selectedValue = model.translateValue(optionValues[i]);
179
180 // Add this element in the list back
181 selectedList.add(selectedValue);
182 }
183 }
184
185 try
186 {
187 getValidatableFieldSupport().validate(this, writer, cycle,
188 selectedList);
189
190 setSelectedList(selectedList);
191 }
192 catch (ValidatorException e)
193 {
194 getForm().getDelegate().record(e);
195 }
196 }
197
198 public abstract IPropertySelectionModel getModel();
199
200 public abstract IMultiplePropertySelectionRenderer getRenderer();
201
202 public abstract void setRenderer(IMultiplePropertySelectionRenderer renderer);
203
204 public abstract ValidatableFieldSupport getValidatableFieldSupport();
205
206 /**
207 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired()
208 */
209 public boolean isRequired()
210 {
211 return getValidatableFieldSupport().isRequired(this);
212 }
213 }