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