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 006package fc.web.forms; 007 008import javax.servlet.*; 009import javax.servlet.http.*; 010import java.io.*; 011import java.util.*; 012 013import fc.jdbc.*; 014import fc.io.*; 015import fc.util.*; 016 017 018/** 019Represents a field that: 020<ol> 021<li>returns one (1) string as their value. Contrast this with 022selects/with/multiple and radiogroups that may return 023a String[]. 024<li> 025In addition, the <i>name</i> of this field is always 026returned on a form submit by the browser (barring browser 027hacks etc). For example, if a text field is not filled out 028by the user, the browser still returns that text field (with 029no value). Contrast this with radio buttons that are not 030returned at all (not even their name) if the user does not 031select them. 032</ol> 033<p>Important note: the {@link trimValues(boolean)} method 034will by default trim whitespace from the beginning/end of 035user input. If the resulting string is empty (""), then 036it will be treated as if the user had not entered anything. 037(which is typically the desired behavior). 038 039@author hursh jain 040**/ 041public abstract class AbstractText extends Field 042{ 043static class Data { 044 String value; 045 } 046 047String orig_value; 048boolean trimValues = true; 049 050AbstractText (String name) { 051 super(name); 052 } 053 054AbstractText (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/** 066Sets 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 069different names in the html form</b>. Mulitple fields with the same name 070are 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 072be saved (and the rest will be ignored)</u> [This is one of - or perhaps 073the only - restriction of this form API; without it, things become too 074complex to manage]. 075*/ 076public void setValueFromSubmit(FormData fd, HttpServletRequest req) 077throws 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/** 112Sets the <b>initial</b> value of this text element to the specified string. 113If the specified value is <tt>null</tt>, then the initial value is set to 114"" (the empty string). 115**/ 116public 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/** 126Sets the selected values for this select in the specified form data. This 127is useful for showing different <i>initial</i> values to each user (before 128the form has been submitted by that user). 129<p> 130If the form has not been submitted, there is no form data object. A form 131data 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*/ 136public 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/** 152Returns a string representing the value of this field or <tt>null</tt> if 153there 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 155if the field is enabled. However if the <i>values</i> are empty, then 156this method will return <tt>null</tt>. For example, if the browser 157returned <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 159string). 160*/ 161public 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/** 173Convenience 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 175no 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*/ 180public String getStringValue(FormData fd) 181 { 182 String s = getValue(fd); 183 if (s == null) 184 s = ""; 185 return s; 186 } 187 188/** 189Convenience method that returns the value of this 190field as a Integer. 191 192@throws NumberFormatException if the value could not be 193 returned as an integer. 194*/ 195public 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/** 203Convenience method that returns the value of this field as a 204Short. 205 206@throws NumberFormatException if the value could not be 207 returned as a short. 208*/ 209public 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/** 217Convenience method that returns the value of this field as a 218Float. 219 220@throws NumberFormatException if the value could not be 221 returned as a float. 222*/ 223public 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/** 232Convenience method that returns the value of this field as a 233Double. 234 235@throws NumberFormatException if the value could not be 236 returned as a double. 237*/ 238public 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/** 246Convenience method that returns the value of this field as a boolean. The 247value is converted into a boolean as per the {@link 248Boolean.valueOf(String)} method. 249*/ 250public boolean getBooleanValue(FormData fd) { 251 return Boolean.valueOf(getValue(fd)).booleanValue(); 252 } 253 254/** 255The value to render this field with. Maintains state and returns 256whatever the user typed in last if applicable. If the form has 257not been shown to the user or if this field is disabled at all, returns 258the original value. 259*/ 260String 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 279public 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 299public 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/** 306Trims leading and ending spaces from all entered values. 307This is <tt>true</tt> by default. Specify <tt>false</tt> to 308turn this off. 309*/ 310public void trimValues(boolean val) 311 { 312 trimValues = val; 313 } 314 315public String toString() 316 { 317 return super.toString() + "; Orig. value: [" + orig_value + "]"; 318 } 319 320}