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.simpleforms;
007
008import javax.servlet.*;
009import javax.servlet.http.*;
010import java.io.*;
011import java.util.*;
012
013import fc.io.*;
014import fc.util.*;
015
016/**
017Helps maintain form state. Form state needs to be maintained
018when a form is submitted but needs to be redisplayed to the
019user because of errors or incomplete fields. 
020<p>
021For 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>
025Often we need to set default/initial values (say from a database) for 
026pre-filling a form before it's shown to the user (for example, when
027the user is editing some information that already exists in the database).
028In the simple form API, this is done via invoking the {@link 
029#set(HttpServletRequest, String, String) set(req, field_name, field_value)} 
030method, which sets the initial value of the field in the specified
031HttpServletRequest object (mimicking as if the user had entered those values
032and submitted the form). Since various widgets maintain state by retrieveing
033these values from the HttpServletRequest, these values will be shown 
034to the user. For example:
035<blockquote><pre>
036String initial_value = databaseQuery.getUserName(); //for example
037State.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*/
047public final class State
048{
049private 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
061private 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  
086private 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  
114private static final String[] str_arr_type = new String[] { };
115
116//---------------------------------------------------------
117
118/**
119Returns the value of the submitted text area (or an empty string if
120no 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*/
125public static final String text(
126final 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/**
138Returns the value of the submitted text area (or an empty string if
139no value was submitted). The returned value is trimmed of all
140leading and trailing spaces.
141<p>
142The returned value replicates what the user entered and is not url
143encoded. This is good when maintaining state.
144<p>
145Of course, when saving this out to the database, we should either 
146strip naughty tags or save it as url encoded text. (otherwise, 
147the door to cross-site scripting hacks is wide open). That's an 
148orthogonal issue to maintaining form state via this method.
149Example:
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*/
156public static final String textarea(
157final 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/**
170Returns the value of the submitted password (or an empty string if
171no 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*/
176public static final String password(
177 final HttpServletRequest req, final String name)
178  {
179  return text(req, name);
180  }
181
182/**
183Returns the submitted state of the submitted radio box. This radio
184box should not be part of a radio group (multiple radio buttons with
185the same name). For radio groups use the {@link #radiogroup
186radiogroup} 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
191presence/absence of the <tt>checked</tt> attribute. The value
192returned by this method will be either an empty string or
193<tt>checked</tt>. This should not be assigned to the 'value'
194attribute (if present) of the radiobox. Rather, it should be a
195stand-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*/
202public 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/**
222Calls {@link #radio(HttpServletRequest, String, boolean) radio(req, name, <b>false</b>)}
223*/
224public static final String radio(
225  final HttpServletRequest req, final String name)
226  {
227  return radio(req, name, false);
228  }
229
230
231/**
232Returns the submitted state of the specified radio box which is part
233of a radio group (multiple radio buttons with the same name). For
234non-group radio boxes, use the {@link #radio radio} method instead.
235Example:
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>
244This method takes a <code>value</code> parameter that specifies the
245value associated with this particular radio box in the radio group.
246Radios 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
249and specified value tag.</u>
250<p>
251<b>Note</b>: For radio boxes, the state maintainence value is the
252presence/absence of the <tt>checked</tt> attribute. The value
253returned by this method will be either an empty string or
254<tt>checked</tt>. This should not be assigned to the 'value'
255attribute (if present) of the radiobox. Rather, it should be a
256stand-alone attribute.
257*/
258public 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/*
280Calls {@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
285be checked every time if we say
286  [=State.checkbox(req, "foo", true)],
287because the true will override things even if the user unchecked
288the box. so state is not maintained, too confusing...
289..use the initial state (set) method to show initially checked boxes.
290
291public static final String checkbox(
292 final HttpServletRequest req, final String name)
293  {
294  return checkbox(req, name, false);
295  }
296*/
297
298/**
299Returns the checked state of the submitted checkbox. This radio box
300should not be part of a radio group (multiple checkboxes with the
301same name). For radio groups use the {@link #checkboxgroup
302checkboxgroup} method.
303Example:
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
308presence/absence of the <tt>checked</tt> attribute. The value
309returned by this method will be either an empty string or
310<tt>checked</tt>. This should not be assigned to the 'value'
311attribute (if present) of checkbox. Rather, it should be a
312stand-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*/
319public 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/**
334Returns the submitted state of the specified check box which is part
335of a checkbox group (multiple check buttons with the same name). For
336non-group check boxes, use the {@link #checkbox checkbox} method instead.
337<p>
338Note: Checkboxes with no specific value are submitted with the value
339"on" (case-insensitive) by the browser (as seen in the second and
340third 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>
351This method takes a <code>value</code> parameter that specifies the
352value of the check box in the checkbox group. <u>This method
353requires that each checkbox in the group have a specified and unique
354value attribute</u>. 
355<p>
356<b>Note</b>: For check boxes, the state maintainence value is the
357presence/absence of the <tt>checked</tt> attribute. The value
358returned by this method will be either an empty string or
359<tt>checked</tt>. This should not be assigned to the 'value'
360attribute (if present) of the checkbox. Rather, it should be a
361stand-alone attribute.
362*/
363public 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/**
394Returns the value of the submitted hidden field or an empty string if no
395value was submitted (this can happen if the user hacked/removed this
396value manually).
397Example:
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*/
402public 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/**
414Returns the state of the submitted select. For select options, we
415need to maintain whichever option(s) were selected by the user. The
416presence/absence of the <tt>selected</tt> attribute maintains this
417state.
418<p>
419When maintaining states for select input types:
420<ol>
421<li>
422We know the select options on the server-side and write them
423out dynamically (or statically) on the server-side and 
424then send the page to the client. To maintain state, we 
425say:
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>
436We query and create select options on the client-side using
437ajax/js/etc (for example, if the client chooses a country, we query
438a database and show the states for that country via another
439client-side select, created dynamically). Upon submission of this
440form, we still need to maintain the state of this option. To do
441this, we need to know, on the server side, the names of any new
442selects that are created on the client (and/or the names of
443initially empty selects that are re-populated on the client).
444Suppose, 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
451On 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
458To maintain the state on the server for this option, we now have to 
459say 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
466But we don't know what value to put in <tt><font color=red>"?"</font></tt>
467on the server-side because <tt><font color=red>Pennsylvania</font></tt> was generated on the 
468client. 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
472and say, for example something like:
473<blockquote>
474<pre>
475[[
476if (...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>
497But this (kind of) defeats the purpose of using ajax because we have to 
498replicate the logic on the server. 
499</li>
500<li>
501Another solution is to write out the possible values to be shown to
502the client as a javascript array on the page. Then the client js can
503show the contents of a an array depending on user choice. This saves
504a ajax call to the database. If we store the results of the query in
505a cache or a static variable, then we don't have to re-run the query
506every time and can simply rewrite the array from the cached query
507results.
508</li>
509<li>
510Another option is that when the form is submitted to the server, it
511sets a hidden field with the submitted value of the select. Upon
512recieving the page, javascript on the browser re-runs the query on
513the client side again and then uses the hidden field to restore the
514state of the select option.
515</li>
516</ol>
517<p>These issue only arise when select elements are being modified
518on 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*/
533public 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/**
554Convenience method that creates a list of select options that
555maintain form state. Example:
556<blockquote><pre>
557[[
558List 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*/
570public 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/**
595Convenience method that creates a list of select options that
596maintain form state. Returns the list of options as a string
597(rather than printing it to a printwriter as some other variants of
598this method do). Example:
599<blockquote><pre>
600[[
601List 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*/
612public 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/**
641Sets the value of the form element with the specified name. This
642method is useful to set initial values for the form. The initial
643values shown to the user can be different per user, for example,
644when the user is editing some data that already exists on the database.
645<p>
646This method can be invoked more than once. This is useful to set
647multiple values for the same field (for example, a select box
648with multiple selections or multiple checkboxes with the same name).
649<blockquote><pre>
650State.set(req, "checkbox1", "1.a");
651State.set(req, "checkbox1", "1.b");
652</pre></blockquote>
653*/
654public 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
683private static final String cleared_key = "_fc.web.simpleforms.cleared";
684  
685/**
686The servlet api is written by retarded monkeys smoking crack....which 
687is why there is no way to modify/remove/change/clear 
688the 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>
691This method changes the state of <b>all</b> fields such that methods
692that return state (text, textarea, radio etc) will act as if the
693form field had not been submitted by the user. However, any values
694set by the {@link set} method <i>after</i> invoking this method
695<i>will</i> be seen.
696<p>
697All this is really useful when form data has been saved and the
698user needs to be shown the same form, but with an empty (fresh)
699form state. So for example:
700<blockquote><pre>
701// At the top of a page
702String field_1 = req.getParameter("field_1");
703String field_2 = req.getParameter("field_2");
704
705Errors err =  validate_and_save(field1, field2);
706if (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>
717Note: redirecting the client to the form page also has the same
718effect (of clearing request parameters), because that forces a 
719entirely new request object to be used on the server.
720*/
721public 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/**
733Convenience method that returns: <tt>{@link 
734fc.util.HTMLUtil.quoteToEntity HTMLUtil.quoteToEntity}(text(req, name));</tt>
735Useful for setting arbitrary values in the value="..." part 
736of a text field.
737*/
738public static final String escapedText(
739final HttpServletRequest req, final String name)
740  {
741  return HTMLUtil.quoteToEntity(text(req, name));
742  }
743
744/**
745Convenience method that returns: <tt>{@link 
746fc.util.HTMLUtil.quoteToEntity(String) HTMLUtil.quoteToEntity}(password (req, name));</tt>
747Useful for setting arbitrary values in the value="..." part 
748of a password field.
749*/
750public static final String escapedPassword(
751final HttpServletRequest req, final String name)
752  {
753  return HTMLUtil.quoteToEntity(password(req, name));
754  }
755
756
757/**
758Convenience method that returns: <tt>{@link 
759fc.util.HTMLUtil.quoteToEntity(String) HTMLUtil.quoteToEntity}(hidden(req, name));</tt>
760Useful for setting arbitrary values in the value="..." part 
761of a hidden field.
762*/
763public static final String escapedHidden(
764final HttpServletRequest req, final String name)
765  {
766  return HTMLUtil.quoteToEntity(hidden(req, name));
767  }
768
769
770private static final String disabled_key = "_fc.web.simpleforms.disabled";
771
772/**
773Convenience method that marks the specified field as disabled. This
774value can be later retrieved while rendering the form via the
775getDisabled or disabled methods.
776*/
777public 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/**
789Returns true if the specified field was marked as disabled. 
790Example usage:
791<blockquote><pre>
792[= State.getDisabled(req, "somefield") ? "disabled" : ""]
793</pre></blockquote>
794*/
795public 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/**
806Convenience method. Instead of saying:
807Example usage:
808<blockquote><pre>
809[= State.getDisabled(req, "somefield") ? "disabled" : ""]
810</pre></blockquote>
811this method allows one to say:
812<blockquote><pre>
813[= State.disabled(req, "somefield")]
814</pre></blockquote>
815*/
816public 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