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 /**
019 Represents a field that:
020 <ol>
021 <li>returns one (1) string as their value. Contrast this with
022 selects/with/multiple and radiogroups that may return
023 a String[].
024 <li>
025 In addition, the <i>name</i> of this field is always
026 returned on a form submit by the browser (barring browser
027 hacks etc). For example, if a text field is not filled out
028 by the user, the browser still returns that text field (with
029 no value). Contrast this with radio buttons that are not
030 returned at all (not even their name) if the user does not
031 select them.
032 </ol>
033 <p>Important note: the {@link trimValues(boolean)} method
034 will by default trim whitespace from the beginning/end of
035 user input. If the resulting string is empty (""), then
036 it will be treated as if the user had not entered anything.
037 (which is typically the desired behavior).
038
039 @author hursh jain
040 **/
041 public abstract class AbstractText extends Field
042 {
043 static class Data {
044 String value;
045 }
046
047 String orig_value;
048 boolean trimValues = true;
049
050 AbstractText (String name) {
051 super(name);
052 }
053
054 AbstractText (String name, String value)
055 {
056 super(name);
057 if (value == null)
058 {
059 log.warn("specified value was null, defaulting to \"\"");
060 value = "";
061 }
062 orig_value = value;
063 }
064
065 /**
066 Sets the value of the field from the submitted form data.
067
068 <b>Note, by default, it is assumed that all abstract text fields have
069 different names in the html form</b>. Mulitple fields with the same name
070 are not supported. <u>If there is more than one field with the same name
071 (such as multiple text fields with the same name), then only 1 value will
072 be saved (and the rest will be ignored)</u> [This is one of - or perhaps
073 the only - restriction of this form API; without it, things become too
074 complex to manage].
075 */
076 public void setValueFromSubmit(FormData fd, HttpServletRequest req)
077 throws SubmitHackedException
078 {
079 String value = req.getParameter(name);
080
081 //can be null if not sent by client for some reason
082 //will be "" is not isFilled by the client since the servlet
083 //api, for the request params 'foo=&bar=, getParam("foo")
084 //returns an empty string (not null).
085 //
086 if (value == null) {
087 //Browsers return no value for disabled fields !!
088 if (! enabled || ! isEnabled(fd))
089 return;
090 //client was buggy or hacked
091 hacklert(req, "Did not find ["+name+"] field in the request (but expected to), defaulting to \"\"");
092 return;
093 }
094
095 if (trimValues) {
096 value = value.trim();
097 }
098
099 //servlet api returns an empty string for param 'foo=' [not null]
100 //we treat that as a null value, we also treat white space
101 //as null if trimming is on.
102 if (value.equals(""))
103 return;
104
105 //we have a non-null value
106 AbstractText.Data data = new AbstractText.Data();
107 fd.putData(name, data);
108 data.value = value;
109 }
110
111 /**
112 Sets the <b>initial</b> value of this text element to the specified string.
113 If the specified value is <tt>null</tt>, then the initial value is set to
114 "" (the empty string).
115 **/
116 public void setValue(String value)
117 {
118 if (value == null) {
119 log.warn("specified value was null, defaulting to \"\"");
120 value = "";
121 }
122 this.orig_value = value;
123 }
124
125 /**
126 Sets the selected values for this select in the specified form data. This
127 is useful for showing different <i>initial</i> values to each user (before
128 the form has been submitted by that user).
129 <p>
130 If the form has not been submitted, there is no form data object. A form
131 data object should be manually created if needed for storing the value.
132
133 @param fd the non-null form data used for rendering the form
134 @param value the value to be set
135 */
136 public void setValue(FormData fd, String value)
137 {
138 Argcheck.notnull(fd, "specified fd param was null");
139
140 if (value == null) {
141 log.warn("specified value was null, defaulting to \"\"");
142 value = "";
143 }
144 AbstractText.Data data = new AbstractText.Data();
145
146 fd.putData(name, data);
147 data.value = value;
148 }
149
150
151 /**
152 Returns a string representing the value of this field or <tt>null</tt> if
153 there is no current value. The <i>names</i> of Form fields like
154 <tt>text</tt> and <tt>textarea</tt> are always sent back by the browser
155 if the field is enabled. However if the <i>values</i> are empty, then
156 this method will return <tt>null</tt>. For example, if the browser
157 returned <xmp>foo=&bar=baz</xmp>, then <tt>foo</tt> will have a
158 <tt>null</tt> value (even though the <i>servlet api</i> returns an empty
159 string).
160 */
161 public String getValue(FormData fd)
162 {
163 AbstractText.Data data = (AbstractText.Data) fd.getData(name);
164
165 if (data == null)
166 return null;
167
168 return data.value;
169 }
170
171
172 /**
173 Convenience method that returns the value of this field as a String.
174 <tt>null</tt> values (ie which can happen when the field is disabled and
175 no value sent by the browser) <i>are returned as an empty string.</i>
176
177 @throws NumberFormatException if the value could not be
178 returned as in integer.
179 */
180 public String getStringValue(FormData fd)
181 {
182 String s = getValue(fd);
183 if (s == null)
184 s = "";
185 return s;
186 }
187
188 /**
189 Convenience method that returns the value of this
190 field as a Integer.
191
192 @throws NumberFormatException if the value could not be
193 returned as an integer.
194 */
195 public int getIntValue(FormData fd) {
196 String value = getValue(fd);
197 if (value != null)
198 value = value.trim();
199 return Integer.parseInt(value);
200 }
201
202 /**
203 Convenience method that returns the value of this field as a
204 Short.
205
206 @throws NumberFormatException if the value could not be
207 returned as a short.
208 */
209 public short getShortValue(FormData fd) {
210 String value = getValue(fd);
211 if (value != null)
212 value = value.trim();
213 return Short.parseShort(value);
214 }
215
216 /**
217 Convenience method that returns the value of this field as a
218 Float.
219
220 @throws NumberFormatException if the value could not be
221 returned as a float.
222 */
223 public float getFloatValue(FormData fd) {
224 String value = getValue(fd);
225 if (value != null)
226 value = value.trim();
227 return Float.parseFloat(value);
228 }
229
230
231 /**
232 Convenience method that returns the value of this field as a
233 Double.
234
235 @throws NumberFormatException if the value could not be
236 returned as a double.
237 */
238 public double getDoubleValue(FormData fd) {
239 String value = getValue(fd);
240 if (value != null)
241 value = value.trim();
242 return Double.parseDouble(value);
243 }
244
245 /**
246 Convenience method that returns the value of this field as a boolean. The
247 value is converted into a boolean as per the {@link
248 Boolean.valueOf(String)} method.
249 */
250 public boolean getBooleanValue(FormData fd) {
251 return Boolean.valueOf(getValue(fd)).booleanValue();
252 }
253
254 /**
255 The value to render this field with. Maintains state and returns
256 whatever the user typed in last if applicable. If the form has
257 not been shown to the user or if this field is disabled at all, returns
258 the original value.
259 */
260 String getRenderValue(FormData fd)
261 {
262 if (! enabled) {
263 return orig_value;
264 }
265
266 if (fd != null)
267 {
268 AbstractText.Data data = (AbstractText.Data) fd.getData(name);
269 if (data == null)
270 return "";
271 else
272 return data.value;
273 }
274 else { //fd == null, no form data, showing form for first time
275 return orig_value;
276 }
277 }
278
279 public boolean isFilled(FormData fd)
280 {
281 AbstractText.Data data = (AbstractText.Data) fd.getData(name);
282
283 //data is created only if the submitted value was not null. If
284 //data is not null, then there was a sumbitted value.
285 if (data == null)
286 return false;
287
288 String value = data.value;
289
290 if (value == null) {
291 log.error("Internal error: unexpected state");
292 }
293
294 //since it's always at least some string -- possibly
295 //all spaces but it'll be non-null
296 return true;
297 }
298
299 public void reset(FormData fd) {
300 AbstractText.Data data = (AbstractText.Data) fd.getData(name);
301 if (data != null)
302 data.value = orig_value;
303 }
304
305 /**
306 Trims leading and ending spaces from all entered values.
307 This is <tt>true</tt> by default. Specify <tt>false</tt> to
308 turn this off.
309 */
310 public void trimValues(boolean val)
311 {
312 trimValues = val;
313 }
314
315 public String toString()
316 {
317 return super.toString() + "; Orig. value: [" + orig_value + "]";
318 }
319
320 }