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    }