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.simpleforms;
007    
008    import javax.servlet.*;
009    import javax.servlet.http.*;
010    import java.io.*;
011    import java.util.*;
012    
013    import fc.io.*;
014    import fc.util.*;
015    
016    /**
017    Helps maintain form state. Form state needs to be maintained
018    when a form is submitted but needs to be redisplayed to the
019    user because of errors or incomplete fields. 
020    <p>
021    For example:
022    <blockquote><pre>
023    &lt;input name=<font color=green>foo</font> type='text' value='<font color=blue>[= State.text(req, "<font color=green>foo</font>")]'</font>&gt; 
024    </pre></blockquote>
025    Often we need to set default/initial values (say from a database) for 
026    pre-filling a form before it's shown to the user (for example, when
027    the user is editing some information that already exists in the database).
028    In the simple form API, this is done via invoking the {@link 
029    #set(HttpServletRequest, String, String) set(req, field_name, field_value)} 
030    method, which sets the initial value of the field in the specified
031    HttpServletRequest object (mimicking as if the user had entered those values
032    and submitted the form). Since various widgets maintain state by retrieveing
033    these values from the HttpServletRequest, these values will be shown 
034    to the user. For example:
035    <blockquote><pre>
036    String initial_value = databaseQuery.getUserName(); //for example
037    State.set(req, "<font color=green>foo</font>", initial_value);
038    
039    ...later in the page...
040    //this will show the initial value set above and also maintain state
041    //when the user changes the value and submits the form
042    &lt;input name=<font color=green>foo</font> type='text' value='<font color=blue>[= State.text(req, "<font color=green>foo</font>")]'</font>&gt; 
043    </pre></blockquote>
044    
045    @author hursh jain
046    */
047    public final class State
048    {
049    private static final boolean dbg  = false;
050            
051    //text should always be at least an empty string. However, can
052    //can be hacked. Also browser return no value for disabled fields
053    //we don't track field enable/disable status and to keep simpleforms
054    //simple, don't have a overloaded method that takes status as a
055    //param. so no hacklerts here.
056    
057    //TEXT, TEXTAREA, PASSWORD, RADIO, CHECKBOX, HIDDEN, SELECT
058    
059    // ------------------------------------------------------------
060    
061    private static final String getParameter(
062      final HttpServletRequest req, final String name)
063      {
064      if (req.getAttribute(cleared_key) == null) //use submitted data ?
065        {
066        final String val = req.getParameter(name);
067        if (val != null)
068          return val;
069        }
070    
071      
072      /*
073      return (String) req.getAttribute(name);   //some value or null
074      //below prevents cut-n-paste typos
075      */
076      final Object val = req.getAttribute(name);
077      try {
078        return (String) val;
079        }
080      catch (ClassCastException e) {
081        throw new RuntimeException("The value of this field [" + name + "] should be set ONLY once (and as a string). You cutting'n'pasting dude ?");
082        }
083        
084      }
085      
086    private static final String[] getParameterValues(
087      final HttpServletRequest req, final String name)
088      {
089      if (req.getAttribute(cleared_key) == null) //use submitted data ?
090        {
091        final String[] val = req.getParameterValues(name);
092        if (val != null) {
093          return val;
094          }
095        }
096        
097      //returns null or String[]
098      final Object obj = req.getAttribute(name);
099      
100      if (obj == null)
101        return null;
102        
103      //in case we only added 1 string for a group
104      if (obj instanceof String) { 
105        String[] arr = new String[1];
106        arr[0] = (String) obj;
107        return arr;
108        }
109      
110      final List list = (ArrayList) obj;
111      return (String[]) list.toArray(str_arr_type); 
112      }
113      
114    private static final String[] str_arr_type = new String[] { };
115    
116    //---------------------------------------------------------
117    
118    /**
119    Returns the value of the submitted text area (or an empty string if
120    no value was submitted). Example:
121    <blockquote><pre>
122    &lt;input type=text name=<font color=green>foo</font> value='[=State.text(req, "<font color=green>foo</font>")]'&gt;
123    </pre></blockquote>
124    */
125    public static final String text(
126    final HttpServletRequest req, final String name)
127      {
128      final String val = getParameter(req, name);
129      if (dbg) System.out.println(name+"="+val);
130    
131      if (val != null) 
132        return val;
133      else
134        return "";
135      }
136    
137    /**
138    Returns the value of the submitted text area (or an empty string if
139    no value was submitted). The returned value is trimmed of all
140    leading and trailing spaces.
141    <p>
142    The returned value replicates what the user entered and is not url
143    encoded. This is good when maintaining state.
144    <p>
145    Of course, when saving this out to the database, we should either 
146    strip naughty tags or save it as url encoded text. (otherwise, 
147    the door to cross-site scripting hacks is wide open). That's an 
148    orthogonal issue to maintaining form state via this method.
149    Example:
150    <blockquote><pre>
151    &lt;textarea name=<font color=green>foo</font>&gt;
152    [=State.textarea(req, "<font color=green>foo</font>")]
153    &lt;/textarea&gt;
154    </pre></blockquote>
155    */
156    public static final String textarea(
157    final HttpServletRequest req, final String name)
158      {
159      //we need to trim(), otherwise
160      // <textarea> NL
161      // [state..   ] NL
162      // </textarea>
163      // The newline keep adding up and adding themselves to the state
164      
165      return text(req, name).trim();
166      }
167    
168    
169    /**
170    Returns the value of the submitted password (or an empty string if
171    no value was submitted). Example:
172    <blockquote><pre>
173    &lt;input type=password name=<font color=green>foo</font> value='[=State.password(req, "<font color=green>foo</font>")]'&gt;
174    </pre></blockquote>
175    */
176    public static final String password(
177     final HttpServletRequest req, final String name)
178      {
179      return text(req, name);
180      }
181    
182    /**
183    Returns the submitted state of the submitted radio box. This radio
184    box should not be part of a radio group (multiple radio buttons with
185    the same name). For radio groups use the {@link #radiogroup
186    radiogroup} method. Example:
187    <blockquote><pre>
188    &lt;input type=radio name=<font color=green>foo</font> value='somevalue' [=State.radio(req, "<font color=green>foo</font>")]&gt;
189    </pre></blockquote>
190    <b>Note</b>: For radio boxes, the state maintainence value is the
191    presence/absence of the <tt>checked</tt> attribute. The value
192    returned by this method will be either an empty string or
193    <tt>checked</tt>. This should not be assigned to the 'value'
194    attribute (if present) of the radiobox. Rather, it should be a
195    stand-alone attribute. 
196    
197    @param  name    the name of the radio element
198    @param  selected  <tt>true</tt> to select this radio box
199              initially (the first time the form is shown to
200              the user), <tt>false</tt> otherwise.
201    */
202    public static final String radio(
203      final HttpServletRequest req, final String name,
204      final boolean selected)
205      {
206      final String val = getParameter(req, name);
207      if (dbg) System.out.println(name+"="+val);
208          
209      if (val == null) 
210        {
211        if (selected)
212          return "checked";
213        else
214          return "";
215        }
216      
217      //value != null (any value)
218      return "checked";
219      }
220    
221    /**
222    Calls {@link #radio(HttpServletRequest, String, boolean) radio(req, name, <b>false</b>)}
223    */
224    public static final String radio(
225      final HttpServletRequest req, final String name)
226      {
227      return radio(req, name, false);
228      }
229    
230    
231    /**
232    Returns the submitted state of the specified radio box which is part
233    of a radio group (multiple radio buttons with the same name). For
234    non-group radio boxes, use the {@link #radio radio} method instead.
235    Example:
236    <blockquote><pre>
237    <b><u>Good:</u></b>
238    &lt;input type=radio name=<font color=green>foo</font> <u>value='<font color=blue>somevalue1</font>'</u> [=State.radiogroup(req, "<font color=green>foo</font>", <font color=blue>"somevalue1"</font>)]&gt;
239    &lt;input type=radio name=<font color=green>foo</font> <u>value='<font color=blue>somevalue2</font>'</u> [=State.radiogroup(req, "<font color=green>foo</font>", <font color=blue>"somevalue2"</font>)]&gt;
240    
241    <b>Don't do this</b>
242    &lt;input type=radio name=<font color=green>foo</font> [=State.radiogroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]&gt;
243    </pre></blockquote>
244    This method takes a <code>value</code> parameter that specifies the
245    value associated with this particular radio box in the radio group.
246    Radios with no specific value are submitted with the value "on"
247    (case-insensitive) by the browser. (as seen in the second example above).
248    <u>This method requires that each radio in the radiogroup have a unique
249    and specified value tag.</u>
250    <p>
251    <b>Note</b>: For radio boxes, the state maintainence value is the
252    presence/absence of the <tt>checked</tt> attribute. The value
253    returned by this method will be either an empty string or
254    <tt>checked</tt>. This should not be assigned to the 'value'
255    attribute (if present) of the radiobox. Rather, it should be a
256    stand-alone attribute.
257    */
258    public static final String radiogroup(
259      final HttpServletRequest req, final String name, final String value)
260      {
261      final String[] val = getParameterValues(req, name);
262    
263      if (dbg) System.out.println(name+"="+Arrays.toString(val));
264      
265      //val could be the value or "on" if radio has no value tag
266      if (val == null)
267        return "";
268      
269      for (int n = 0; n < val.length; n++) 
270        {
271        if (val[n].equals(value) || val[n].equalsIgnoreCase("on")) {
272          return "checked";
273          }
274        }
275      
276      return "";
277      }
278    
279    /*
280    Calls {@link checkbox(HttpServletRequest, String, boolean)} as
281    <tt>checkbox(HttpServletRequest, String, <b>false</b>)</tt>
282    */
283    /*
284    ..thot it was convenient but it's useless...the checkbox will
285    be checked every time if we say
286      [=State.checkbox(req, "foo", true)],
287    because the true will override things even if the user unchecked
288    the box. so state is not maintained, too confusing...
289    ..use the initial state (set) method to show initially checked boxes.
290    
291    public static final String checkbox(
292     final HttpServletRequest req, final String name)
293      {
294      return checkbox(req, name, false);
295      }
296    */
297    
298    /**
299    Returns the checked state of the submitted checkbox. This radio box
300    should not be part of a radio group (multiple checkboxes with the
301    same name). For radio groups use the {@link #checkboxgroup
302    checkboxgroup} method.
303    Example:
304    <blockquote><pre>
305    &lt;input type=checkbox name=<font color=green>foo</font> value='somevalue' [=State.checkbox(req, "<font color=green>foo</font>")]&gt;
306    </pre></blockquote>
307    <b>Note</b>: For check boxes, the state maintainence value is the
308    presence/absence of the <tt>checked</tt> attribute. The value
309    returned by this method will be either an empty string or
310    <tt>checked</tt>. This should not be assigned to the 'value'
311    attribute (if present) of checkbox. Rather, it should be a
312    stand-alone attribute as shown above.
313    
314    @param  name    the name of the radio element
315    @param  selected  <tt>true</tt> to select this checkbox box
316              initially (the first time the form is shown to
317              the user), <tt>false</tt> otherwise.
318    */
319    public static final String checkbox(
320     final HttpServletRequest req, final String name)
321      {
322      final String val = getParameter(req, name);
323      if (dbg) System.out.println(name+"="+val);
324      
325      if (val == null) {
326        return "";
327        }
328      
329      //value != null (any value)
330      return "checked";
331      }
332    
333    /**
334    Returns the submitted state of the specified check box which is part
335    of a checkbox group (multiple check buttons with the same name). For
336    non-group check boxes, use the {@link #checkbox checkbox} method instead.
337    <p>
338    Note: Checkboxes with no specific value are submitted with the value
339    "on" (case-insensitive) by the browser (as seen in the second and
340    third example below). Example:
341    <blockquote><pre>
342    
343    <b><u>Good</u></b>:
344    &lt;input type=checkbox name=<font color=green>foo</font> <u>value='<font color=blue>somevalue1</font>'</u> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"somevalue1"</font>)]&gt;
345    &lt;input type=checkbox name=<font color=green>foo</font> <u>value='<font color=blue>somevalue2</font>'</u> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"somevalue2"</font>)]&gt;
346    
347    <b>Don't do this</b>:
348    &lt;input type=checkbox name=<font color=green>foo</font> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]&gt;
349    &lt;input type=checkbox name=<font color=green>foo</font> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]&gt;
350    </pre></blockquote>
351    This method takes a <code>value</code> parameter that specifies the
352    value of the check box in the checkbox group. <u>This method
353    requires that each checkbox in the group have a specified and unique
354    value attribute</u>. 
355    <p>
356    <b>Note</b>: For check boxes, the state maintainence value is the
357    presence/absence of the <tt>checked</tt> attribute. The value
358    returned by this method will be either an empty string or
359    <tt>checked</tt>. This should not be assigned to the 'value'
360    attribute (if present) of the checkbox. Rather, it should be a
361    stand-alone attribute.
362    */
363    public static final String checkboxgroup(
364      final HttpServletRequest req, final String name, final String value)
365      {
366      final String[] val = getParameterValues(req, name);
367      if (dbg) System.out.println(name+"="+Arrays.toString(val));
368      
369      if (val == null)
370        return "";
371      
372      /*
373      we cannot say:
374      
375        if (val[n].equals(value) || val[n].equalsIgnoreCase("on")) ...
376      
377      because a checkbox with no value - if checked - will also check all
378      other checkboxes in the group (since we get foo=on and we don't
379      know which checkbox to turn on in that case, since they are all 
380      called foo)
381      */
382      for (int n = 0; n < val.length; n++) 
383        {
384        if (val[n].equals(value)) {
385          return "checked";
386          }
387        }
388      
389      return "";
390      }
391    
392    
393    /**
394    Returns the value of the submitted hidden field or an empty string if no
395    value was submitted (this can happen if the user hacked/removed this
396    value manually).
397    Example:
398    <blockquote><pre>
399    &lt;input type=hidden name=<font color=green>foo</font> value='[=State.hidden(req, "<font color=green>foo</font>")]'&gt;
400    </pre></blockquote>
401    */
402    public static final String hidden(final HttpServletRequest req, final String name)
403      {
404      return text(req, name);
405      }
406    
407    
408    //<input type=image: get x,y co-ordinates of the click. not needed for form
409    //maintainence
410    //<input type=submit: not needed for form maintainence
411    
412    
413    /**
414    Returns the state of the submitted select. For select options, we
415    need to maintain whichever option(s) were selected by the user. The
416    presence/absence of the <tt>selected</tt> attribute maintains this
417    state.
418    <p>
419    When maintaining states for select input types:
420    <ol>
421    <li>
422    We know the select options on the server-side and write them
423    out dynamically (or statically) on the server-side and 
424    then send the page to the client. To maintain state, we 
425    say:
426    <blockquote>
427    <pre>
428    &lt;select name=<font color=green>sel1</font>&gt;
429    &lt;option value=<font color=red>"1"</font> <font color=blue>[=State.select(req, <font color=green>"sel1"</font>, <font color=red>"1")</font>]</font>&gt; Option 1
430    &lt;option           <font color=blue>[=State.select(req, <font color=green>"sel1"</font>, <font color=red>"Option 1"</font>)]</font>&gt; <font color=red>Option 1</font>
431    &lt;/select&gt;
432    </pre>
433    </blockquote>
434    
435    <li>
436    We query and create select options on the client-side using
437    ajax/js/etc (for example, if the client chooses a country, we query
438    a database and show the states for that country via another
439    client-side select, created dynamically). Upon submission of this
440    form, we still need to maintain the state of this option. To do
441    this, we need to know, on the server side, the names of any new
442    selects that are created on the client (and/or the names of
443    initially empty selects that are re-populated on the client).
444    Suppose, initially we have:
445    
446    <blockquote><pre>
447    &lt;select name=<font color=green>states</font>&gt;
448    &lt;/select&gt;
449    </pre></blockquote>
450    
451    On the client, we then add:
452    <blockquote><pre>
453    &lt;select name=<font color=green>states</font>&gt;
454    &lt;option&gt; <font color=red>Pennsylvania</font> (generated on client)
455    &lt;/select&gt;
456    </pre></blockquote>
457    
458    To maintain the state on the server for this option, we now have to 
459    say on the server-side (upon form submission):
460    <blockquote><pre>
461    &lt;select name=<font color=green>states</font>&gt;
462       &lt;option <font color=blue>[=State.select(req, <font color=green>"states"</font>, </font><font color=red>"?"</font>)]&gt;<font color=red>Pennsylvania</font> (generated on client) 
463    &lt;select>
464    </pre></blockquote>
465    
466    But we don't know what value to put in <tt><font color=red>"?"</font></tt>
467    on the server-side because <tt><font color=red>Pennsylvania</font></tt> was generated on the 
468    client. So we can do any of the following:
469    <style>ol ol li { padding-top: 1em; }</style>
470    <ol style="list-style: square">
471    <li>Rerun the query on the server, get the list of states again
472    and say, for example something like:
473    <blockquote>
474    <pre>
475    [[
476    if (...options were added by by client...)
477        { //maintain state
478        List option_list == ...create new {@link SelectOption}'s here [possibly via a query]...
479        out.print(" &lt;select name=<font color=green>states</font>&gt; ");
480        for (int n = 0; n < option_list.size(); n++) 
481            {
482            {@link SelectOption} opt = (SelectOption) option_list.get(n);
483            String html = opt.getHTML();
484            String val  = opt.getValue();
485            out.print(" &lt;option val=");
486            our.print(val);
487            if (<font color=blue>State.select(req, <font color=green>"states"</font>, val)</font>) {
488                out.print(" selected ");
489                }
490            out.print(" &gt; ");
491            out.print(html);
492            }
493        out.print(" &lt;/select&gt; ");
494        }
495    ]]
496    </pre></blockquote>
497    But this (kind of) defeats the purpose of using ajax because we have to 
498    replicate the logic on the server. 
499    </li>
500    <li>
501    Another solution is to write out the possible values to be shown to
502    the client as a javascript array on the page. Then the client js can
503    show the contents of a an array depending on user choice. This saves
504    a ajax call to the database. If we store the results of the query in
505    a cache or a static variable, then we don't have to re-run the query
506    every time and can simply rewrite the array from the cached query
507    results.
508    </li>
509    <li>
510    Another option is that when the form is submitted to the server, it
511    sets a hidden field with the submitted value of the select. Upon
512    recieving the page, javascript on the browser re-runs the query on
513    the client side again and then uses the hidden field to restore the
514    state of the select option.
515    </li>
516    </ol>
517    <p>These issue only arise when select elements are being modified
518    on the client side via Javascript. 
519    </li>
520    </ol>
521    
522    @param  req       the HttpServletRequest 
523    @param  selectName    the name of the select field
524    @param  optionVal   the value attribute of the option. If the
525                option has no option attribute, the html
526                text associated with that option. Browsers
527                do not submit leading spaces in the html
528                text so, for example for: <tt>
529                "&lt;option&gt;&nbsp;&nbsp;Option 1"</tt>, the
530                value would be "Option 1"
531    
532    */
533    public static final String select(final HttpServletRequest req, 
534            final String selectName, final String optionVal)
535      {
536      final String[] val = getParameterValues(req, selectName);
537      if (dbg) System.out.println(selectName+"="+Arrays.toString(val));
538      
539      if (val == null)
540        return "";
541      
542      for (int n = 0; n < val.length; n++) {
543        if (dbg) System.out.println(val[n] + ";" + optionVal + ";" + (optionVal.equals(val[n]) ? "true/checked" : "false") );
544        if (optionVal.equals(val[n])) {
545          return "selected";
546          }
547        }
548      
549      return "";
550      }
551    
552    
553    /**
554    Convenience method that creates a list of select options that
555    maintain form state. Example:
556    <blockquote><pre>
557    [[
558    List list = (..create a list of {@link SelectOption}, possible from database lookup..)
559    ]]
560    &lt;select name=foo&gt;
561    [[ makeOptions(out, req, "foo", list); ]]
562    &lt;/select&gt;
563    </pre></blockquote>
564    
565    @param  out     destination where the options will be printed
566    @param  req     the current HttpServletRequest
567    @param  selectName  the name of the select field
568    @param  optionsList a list of objects of type {@link SelectOption}
569    */
570    public static void makeOptions(final PrintWriter out,
571     final HttpServletRequest req, 
572     final String selectName, final List optionList)
573      {
574      final int size = optionList.size();
575      for (int n = 0; n < size; n++) 
576        {
577        SelectOption opt = (SelectOption) optionList.get(n);
578        String html = HTMLUtil.quoteToEntity(opt.getHTML());
579        String val = opt.getValue();
580        String escaped_val = HTMLUtil.quoteToEntity(val);
581        out.print("<option val='");
582        out.print(escaped_val);
583        out.print("'");
584        out.print(opt.selected() ? " selected " : " ");
585        out.print(State.select(req, selectName, val));
586        out.print(">");
587        out.print(html);
588        if ( (n + 1) < size) {
589          out.print("\n");
590          }
591        }
592      }
593    
594    /**
595    Convenience method that creates a list of select options that
596    maintain form state. Returns the list of options as a string
597    (rather than printing it to a printwriter as some other variants of
598    this method do). Example:
599    <blockquote><pre>
600    [[
601    List list = (..create a list of {@link SelectOption}, possible from database lookup..)
602    ]]
603    &lt;select name=foo&gt;
604    [= makeOptions(req, "foo", list); ]
605    &lt;/select&gt;
606    </pre></blockquote>
607    
608    @param  req     the current HttpServletRequest
609    @param  selectName  the name of the select field
610    @param  optionsList a list of objects of type {@link SelectOption}
611    */
612    public static String makeOptions(final HttpServletRequest req, 
613     final String selectName, final List optionList)
614      {
615      final int size = optionList.size();
616      final StringBuilder buf = new StringBuilder(size * 32);
617      for (int n = 0; n < size; n++) 
618        {
619        SelectOption opt = (SelectOption) optionList.get(n);
620        String html = HTMLUtil.quoteToEntity(opt.getHTML());
621        String val = opt.getValue();
622        String escaped_val = HTMLUtil.quoteToEntity(val);
623        buf.append("<option val='");
624        buf.append(escaped_val);
625        buf.append("'");
626        buf.append(opt.selected() ? " selected " : " ");
627        buf.append(State.select(req, selectName, val));
628        buf.append(">");
629        buf.append(html);
630        if ( (n + 1) < size) {
631          buf.append("\n");
632          }
633        }
634      return buf.toString();
635      }
636    
637    
638    
639    
640    /**
641    Sets the value of the form element with the specified name. This
642    method is useful to set initial values for the form. The initial
643    values shown to the user can be different per user, for example,
644    when the user is editing some data that already exists on the database.
645    <p>
646    This method can be invoked more than once. This is useful to set
647    multiple values for the same field (for example, a select box
648    with multiple selections or multiple checkboxes with the same name).
649    <blockquote><pre>
650    State.set(req, "checkbox1", "1.a");
651    State.set(req, "checkbox1", "1.b");
652    </pre></blockquote>
653    */
654    public static final void set(
655     final HttpServletRequest req, final String name, final String value)
656      {
657      final Object obj = req.getAttribute(name);
658      
659      if (obj == null)  {  //obj not already present
660        req.setAttribute(name, value);
661        return;
662        }
663    
664      //obj is present. could be a single string or string[] (list)
665      if (obj instanceof String) 
666        {
667        //defaults to size of 10 which is fine for us
668        final ArrayList list = new ArrayList();
669        
670        list.add(obj);   //save old single string value
671        list.add(value); //save new value
672        req.setAttribute(name, list);
673        }
674      else if (obj instanceof ArrayList) {
675        //append new string value to the list
676        ((ArrayList)obj).add(value);
677        }
678      else{ 
679        throw new IllegalArgumentException("Only strings can be added. You are trying to add: " + obj + "/" + obj.getClass().getName());
680        }
681      }
682    
683    private static final String cleared_key = "_fc.web.simpleforms.cleared";
684      
685    /**
686    The servlet api is written by retarded monkeys smoking crack....which 
687    is why there is no way to modify/remove/change/clear 
688    the parameters in the servlet request (the parameters map is read-only)
689    ..which leads to all sort of problems in html form processing.
690    <p>
691    This method changes the state of <b>all</b> fields such that methods
692    that return state (text, textarea, radio etc) will act as if the
693    form field had not been submitted by the user. However, any values
694    set by the {@link set} method <i>after</i> invoking this method
695    <i>will</i> be seen.
696    <p>
697    All this is really useful when form data has been saved and the
698    user needs to be shown the same form, but with an empty (fresh)
699    form state. So for example:
700    <blockquote><pre>
701    // At the top of a page
702    String field_1 = req.getParameter("field_1");
703    String field_2 = req.getParameter("field_2");
704    
705    Errors err =  validate_and_save(field1, field2);
706    if (err == null) {
707        State.<font color=blue>clear</font>(req);  //req is the HttpServletRequest object
708        State.set("field_1", "enter a value"); //initial value for field_1
709        }
710    
711    &lt;form&gt;
712    [[ if (err != null) { err.renderFormErrors(out); } ]]
713    &lt;input type=text name=field_1 value='[=State.text(req, "field_1")]'&gt;
714    &lt;input type=text name=field_2 value='[=State.text(req, "field_2")]'&gt;
715    &lt;/form&gt;
716    </pre></blockquote>
717    Note: redirecting the client to the form page also has the same
718    effect (of clearing request parameters), because that forces a 
719    entirely new request object to be used on the server.
720    */
721    public static final void clear(final HttpServletRequest req)
722      {
723      Enumeration e = req.getAttributeNames();
724      while (e.hasMoreElements()) {
725        req.removeAttribute((String)e.nextElement());
726        }
727        
728      req.setAttribute(cleared_key, "");
729      }
730      
731    // ----------- escaped ----------------
732    /**
733    Convenience method that returns: <tt>{@link 
734    fc.util.HTMLUtil.quoteToEntity HTMLUtil.quoteToEntity}(text(req, name));</tt>
735    Useful for setting arbitrary values in the value="..." part 
736    of a text field.
737    */
738    public static final String escapedText(
739    final HttpServletRequest req, final String name)
740      {
741      return HTMLUtil.quoteToEntity(text(req, name));
742      }
743    
744    /**
745    Convenience method that returns: <tt>{@link 
746    fc.util.HTMLUtil.quoteToEntity(String) HTMLUtil.quoteToEntity}(password (req, name));</tt>
747    Useful for setting arbitrary values in the value="..." part 
748    of a password field.
749    */
750    public static final String escapedPassword(
751    final HttpServletRequest req, final String name)
752      {
753      return HTMLUtil.quoteToEntity(password(req, name));
754      }
755    
756    
757    /**
758    Convenience method that returns: <tt>{@link 
759    fc.util.HTMLUtil.quoteToEntity(String) HTMLUtil.quoteToEntity}(hidden(req, name));</tt>
760    Useful for setting arbitrary values in the value="..." part 
761    of a hidden field.
762    */
763    public static final String escapedHidden(
764    final HttpServletRequest req, final String name)
765      {
766      return HTMLUtil.quoteToEntity(hidden(req, name));
767      }
768    
769    
770    private static final String disabled_key = "_fc.web.simpleforms.disabled";
771    
772    /**
773    Convenience method that marks the specified field as disabled. This
774    value can be later retrieved while rendering the form via the
775    getDisabled or disabled methods.
776    */
777    public static final void setDisabled(
778     final HttpServletRequest req, final String name)
779      {
780      Set set = (Set) req.getAttribute(disabled_key);
781      if (set == null) {
782        set = new HashSet();
783        req.setAttribute(disabled_key, set);
784        }
785      set.add(name);
786      }
787    
788    /**
789    Returns true if the specified field was marked as disabled. 
790    Example usage:
791    <blockquote><pre>
792    [= State.getDisabled(req, "somefield") ? "disabled" : ""]
793    </pre></blockquote>
794    */
795    public static final boolean getDisabled(
796     final HttpServletRequest req, final String name)
797      {
798      Set set = (Set) req.getAttribute(disabled_key);
799      if (set == null) {
800        return false;
801        }
802      return set.contains(name);
803      }
804    
805    /**
806    Convenience method. Instead of saying:
807    Example usage:
808    <blockquote><pre>
809    [= State.getDisabled(req, "somefield") ? "disabled" : ""]
810    </pre></blockquote>
811    this method allows one to say:
812    <blockquote><pre>
813    [= State.disabled(req, "somefield")]
814    </pre></blockquote>
815    */
816    public static final String disabled(
817     final HttpServletRequest req, final String name)
818      {
819      if (getDisabled(req, name))
820        return "disabled";
821      else 
822        return "";
823      }
824    
825    } //~class State