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