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.form;
016
017 import java.util.HashSet;
018 import java.util.Set;
019
020 import org.apache.hivemind.ApplicationRuntimeException;
021 import org.apache.tapestry.IMarkupWriter;
022 import org.apache.tapestry.IRequestCycle;
023 import org.apache.tapestry.Tapestry;
024 import org.apache.tapestry.valid.ValidatorException;
025
026 /**
027 * Implements a component that manages an HTML <select> form element. The most common
028 * situation, using a <select> to set a specific property of some object, is best handled
029 * using a {@link PropertySelection}component. [ <a
030 * href="../../../../../ComponentReference/Select.html">Component Reference </a>]
031 * <p>
032 * Otherwise, this component is very similar to {@link RadioGroup}.
033 * <p>
034 * As of 4.0, this component can be validated.
035 *
036 * @author Howard Lewis Ship
037 * @author Paul Ferraro
038 */
039 public abstract class Select extends AbstractFormComponent implements ValidatableField
040 {
041
042 /**
043 * Used by the <code>Select</code> to record itself as a {@link IRequestCycle}attribute, so
044 * that the {@link Option}components it wraps can have access to it.
045 */
046
047 private static final String ATTRIBUTE_NAME = "org.apache.tapestry.active.Select";
048
049 private boolean _rewinding;
050
051 private boolean _rendering;
052
053 private Set _selections;
054
055 private int _nextOptionId;
056
057 public static Select get(IRequestCycle cycle)
058 {
059 return (Select) cycle.getAttribute(ATTRIBUTE_NAME);
060 }
061
062 public abstract boolean isMultiple();
063
064 public boolean isRewinding()
065 {
066 if (!_rendering)
067 throw Tapestry.createRenderOnlyPropertyException(this, "rewinding");
068
069 return _rewinding;
070 }
071
072 public String getNextOptionId()
073 {
074 if (!_rendering)
075 throw Tapestry.createRenderOnlyPropertyException(this, "nextOptionId");
076
077 // Return it as a hex value.
078
079 return Integer.toString(_nextOptionId++);
080 }
081
082 public boolean isSelected(String value)
083 {
084 if (_selections == null)
085 return false;
086
087 return _selections.contains(value);
088 }
089
090 /**
091 * @see org.apache.tapestry.AbstractComponent#prepareForRender(org.apache.tapestry.IRequestCycle)
092 */
093 protected void prepareForRender(IRequestCycle cycle)
094 {
095 if (cycle.getAttribute(ATTRIBUTE_NAME) != null)
096 throw new ApplicationRuntimeException(Tapestry.getMessage("Select.may-not-nest"), this,
097 null, null);
098
099 cycle.setAttribute(ATTRIBUTE_NAME, this);
100
101 _rendering = true;
102 _nextOptionId = 0;
103 }
104
105 /**
106 * @see org.apache.tapestry.AbstractComponent#cleanupAfterRender(org.apache.tapestry.IRequestCycle)
107 */
108 protected void cleanupAfterRender(IRequestCycle cycle)
109 {
110 _rendering = false;
111 _selections = null;
112
113 cycle.removeAttribute(ATTRIBUTE_NAME);
114 }
115
116 /**
117 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
118 */
119 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle)
120 {
121 _rewinding = false;
122
123 renderDelegatePrefix(writer, cycle);
124
125 writer.begin("select");
126
127 writer.attribute("name", getName());
128
129 if (isMultiple())
130 writer.attribute("multiple", "multiple");
131
132 if (isDisabled())
133 writer.attribute("disabled", "disabled");
134
135 renderIdAttribute(writer, cycle);
136
137 renderDelegateAttributes(writer, cycle);
138
139 getValidatableFieldSupport().renderContributions(this, writer, cycle);
140
141 renderInformalParameters(writer, cycle);
142
143 renderBody(writer, cycle);
144
145 writer.end();
146
147 renderDelegateSuffix(writer, cycle);
148 }
149
150 /**
151 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
152 */
153 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
154 {
155 _selections = null;
156 _rewinding = true;
157
158 String[] parameters = cycle.getParameters(getName());
159
160 try
161 {
162 if (parameters != null)
163 {
164 int length = parameters.length;
165
166 _selections = new HashSet((length > 30) ? 101 : 7);
167
168 for (int i = 0; i < length; i++)
169 _selections.add(parameters[i]);
170 }
171
172 renderBody(writer, cycle);
173
174 // This is atypical validation - since this component does not explicitly bind to an object
175 getValidatableFieldSupport().validate(this, writer, cycle, parameters);
176 }
177 catch (ValidatorException e)
178 {
179 getForm().getDelegate().record(e);
180 }
181 }
182
183 /**
184 * Injected.
185 */
186 public abstract ValidatableFieldSupport getValidatableFieldSupport();
187
188 /**
189 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired()
190 */
191 public boolean isRequired()
192 {
193 return getValidatableFieldSupport().isRequired(this);
194 }
195 }