001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org)
002 // The Molly framework is freely distributable under the terms of an
003 // MIT-style license. For details, see the molly pages web site at:
004 // http://www.mollypages.org/. Use, modify, have fun !
005
006 package fc.web.forms;
007
008 import javax.servlet.*;
009 import javax.servlet.http.*;
010 import java.io.*;
011 import java.util.*;
012
013 import fc.jdbc.*;
014 import fc.io.*;
015 import fc.util.*;
016
017 /**
018 Abstracts an HTML choice type such as choicebox or radio.
019 Does <b>not</b> abstract a HTML select element for which the
020 {@link Select} class should be used. Concrete subclasses
021 represent a particular choice element such as {@link
022 Checkbox} and {@link Radio}
023
024 @author hursh jain
025 **/
026 public abstract class Choice extends Field
027 {
028 //if this choice object's original state == selected
029 private boolean orig_selected;
030 //can be null if not specified
031 private String value;
032
033 static class Data {
034 //the value actually submitted by the browser (could
035 //be 'on', 'ON' etc., or the value for that option (if set)
036 private String submit_value;
037 }
038
039 /**
040 Creates a new choice object.
041
042 @param name the field name
043 @param value the value of this choice item (can be null
044 for unspecified)
045 @param selected <tt>true</tt> is this choice is
046 originally selected
047 **/
048 protected Choice(String name, String value, boolean selected)
049 {
050 super(name);
051 this.value = value;
052 this.orig_selected = selected;
053 }
054
055 public abstract Field.Type getType();
056
057 /**
058 Returns the current <b>value</b> of this field. This can be:
059 <blockquote>
060 <li><tt>null</tt>: if no value is currently set which can happen if
061 this field was not selected when the parent form was submitted.
062 Browsers send nothing at all if choice type fields are not selected
063 in an HTML form.
064 <li>the value attribute of this field (if this choice field was
065 created/displayed with a value attribute) or the string "on" (which
066 is sent by browsers if there is no specific value attribute for this
067 choice field). Note, the default value should be treated as case
068 <u>in</u>sensitive, since browsers can send <tt>on</tt>, <tt>ON</tt>
069 etc.
070 </blockquote>
071 **/
072 public String getValue(FormData fd)
073 {
074 Choice.Data data = (Choice.Data) fd.getData(name);
075
076 if (data == null)
077 return null;
078
079 return data.submit_value;
080 }
081
082 /**
083 Sets the <u>selected or non-selected state</u> for this choice in the
084 specified form data. Selected choices are returned by the browser as
085 <tt>on, ON, oN</tt> etc., the value returned is not important as long as
086 something is returned. Therefore <i>any non-null value</i> set by this method
087 will have the effect of selecting this choice when it is rendered. A <tt>
088 null</tt> value wil unselect this choice.
089
090 @param fd a non-null form data object
091 @param value any non-null value
092 */
093 public void setValue(FormData fd, String value)
094 {
095 Argcheck.notnull(fd, "specified fd param was null");
096
097 if (value == null)
098 return;
099
100 Choice.Data data = new Choice.Data();
101 fd.putData(name, data);
102 data.submit_value = value;
103 }
104
105 /**
106 Convenience method that sets this choice to be selected/non-selected.
107
108 @param fd a non-null form data object
109 @param selected true to select this choice, false otherwise
110 */
111 public void setValue(FormData fd, boolean selected)
112 {
113 Argcheck.notnull(fd, "specified fd param was null");
114
115 if (selected) {
116 Choice.Data data = new Choice.Data();
117 fd.putData(name, data);
118 //not submit_value = value since value can be null
119 data.submit_value = "on";
120 }
121 }
122
123 /**
124 Sets the value for this choice.
125 */
126 public void setValue(String value, boolean selected)
127 {
128 Argcheck.notnull(value, "specified value was null");
129
130 this.value = value;
131 this.orig_selected = selected;
132 }
133
134 /**
135 Convenience method that returns the value of this
136 field as a Integer.
137
138 @throws NumberFormatException if the value could not be
139 returned as in integer.
140 */
141 public int getIntValue(FormData fd) {
142 String value = getValue(fd);
143 if (value != null)
144 value = value.trim();
145 return Integer.parseInt(value);
146 }
147
148 /**
149 Convenience method that returns the value of this
150 field as a Short.
151
152 @throws NumberFormatException if the value could not be
153 returned as a short.
154 */
155 public short getShortValue(FormData fd) {
156 String value = getValue(fd);
157 if (value != null)
158 value = value.trim();
159 return Short.parseShort(value);
160 }
161
162
163 /**
164 Convenience method that returns the value of this field as a boolean.
165 <u>The returned value will be <tt>true</tt> if the submitted value is
166 "true" or "on" (both case insensitive), else <tt>false</tt></u>
167 */
168 public boolean getBooleanValue(FormData fd)
169 {
170 String value = getValue(fd);
171 if (value == null)
172 return false;
173
174 if (value.equalsIgnoreCase("on"))
175 value = "true";
176
177 return Boolean.valueOf(value).booleanValue();
178 }
179
180 //the value actually submitted by the browser (could
181 //be 'on', 'ON' etc., or the value for that option if set
182 public void setValueFromSubmit(FormData fd, HttpServletRequest req)
183 throws SubmitHackedException
184 {
185 String submittedValue = req.getParameter(name);
186
187 //choice was not selected
188 if (submittedValue == null)
189 return;
190
191 Choice.Data data = new Choice.Data();
192 fd.putData(name, data);
193
194 if (value != null && ! value.equals(submittedValue))
195 hacklert(req, "submitted value: [" + submittedValue + "] does not equal the value [" + value + "]of this field.");
196
197 data.submit_value = submittedValue;
198 //log.bug(name, "setting data=", submittedValue);
199 }
200
201 public void renderImpl(FormData fd, Writer writer) throws IOException
202 {
203 boolean selected = false;
204
205 //no formdata, rendering first time
206 if (fd == null) {
207 selected = this.orig_selected;
208 }
209 else { //submit or initial data
210 String returned_value = getValue(fd); //can be null
211 if (returned_value != null)
212 selected = true;
213 }
214
215 writer.write("<input type='");
216 writer.write(getType().toString());
217 writer.write("' name='");
218 writer.write(name);
219 writer.write("'");
220
221 if (value != null) {
222 //we always write the orig. value, not the returned
223 //one (since the returned value should be the same
224 //as the original value unless hacked)
225 writer.write(" value='");
226 writer.write(value);
227 writer.write("'");
228 }
229
230 if (! enabled || ! isEnabled(fd)) {
231 writer.write(" disabled");
232 }
233
234 if (selected) {
235 writer.write(" checked");
236 }
237
238 if (renderStyleTag) {
239 writer.write(" style='");
240 writer.write(styleTag);
241 writer.write("'");
242 }
243
244 final int arlen = arbitraryString.size();
245 for (int n = 0; n < arlen; n++) {
246 writer.write(" ");
247 writer.write(arbitraryString.get(n).toString());
248 }
249
250 writer.write(">");
251 writer.write("</input>");
252 }
253
254
255 /**
256 @return <tt>true</tt> if this field is selected,
257 <tt>false</tt> otherwise
258 **/
259 public boolean isFilled(FormData fd)
260 {
261 return getValue(fd) != null;
262 }
263
264 /**
265 Sets the <b>initial</b> selection status for this field.
266
267 @param select <tt>true</tt> if this field should be selected
268 <tt>false</tt> otherwise.
269 **/
270 public void setSelected(boolean select) {
271 this.orig_selected = select;
272 }
273
274
275 public String toString()
276 {
277 return super.toString() +
278 "; Orig. value: [" + value + "]" +
279 "; Orig. selected: [" + orig_selected + "]";
280 }
281
282 }