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 jakarta.servlet.*;
009import jakarta.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=<span style="color:green">foo</span> type='text' value='<span style="color:blue">[= State.text(req, "<span style="color:green">foo</span>")]'</span>&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, "<span style="color:green">foo</span>", 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=<span style="color:green">foo</span> type='text' value='<span style="color:blue">[= State.text(req, "<span style="color:green">foo</span>")]'</span>&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=<span style="color:green">foo</span> value='[=State.text(req, "<span style="color:green">foo</span>")]'&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=<span style="color:green">foo</span>&gt;
152[=State.textarea(req, "<span style="color:green">foo</span>")]
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=<span style="color:green">foo</span> value='[=State.password(req, "<span style="color:green">foo</span>")]'&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=<span style="color:green">foo</span> value='somevalue' [=State.radio(req, "<span style="color:green">foo</span>")]&gt;
189</pre></blockquote>
190<b>Note</b>: For radio boxes, the state maintainence value is the
191presence/absence of the <code>checked</code> attribute. The value
192returned by this method will be either an empty string or
193<code>checked</code>. 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  <code>true</code> to select this radio box
199          initially (the first time the form is shown to
200          the user), <code>false</code> 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=<span style="color:green">foo</span> <u>value='<span style="color:blue">somevalue1</span>'</u> [=State.radiogroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"somevalue1"</span>)]&gt;
239&lt;input type=radio name=<span style="color:green">foo</span> <u>value='<span style="color:blue">somevalue2</span>'</u> [=State.radiogroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"somevalue2"</span>)]&gt;
240
241<b>Don't do this</b>
242&lt;input type=radio name=<span style="color:green">foo</span> [=State.radiogroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"on"</span>)]&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 <code>checked</code> attribute. The value
253returned by this method will be either an empty string or
254<code>checked</code>. 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=<span style="color:green">foo</span> value='somevalue' [=State.checkbox(req, "<span style="color:green">foo</span>")]&gt;
306</pre></blockquote>
307<b>Note</b>: For check boxes, the state maintainence value is the
308presence/absence of the <code>checked</code> attribute. The value
309returned by this method will be either an empty string or
310<code>checked</code>. 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*/
316public static final String checkbox(
317 final HttpServletRequest req, final String name)
318  {
319  final String val = getParameter(req, name);
320  if (dbg) System.out.println(name+"="+val);
321  
322  if (val == null) {
323    return "";
324    }
325  
326  //value != null (any value)
327  return "checked";
328  }
329
330/**
331Returns the submitted state of the specified check box which is part
332of a checkbox group (multiple check buttons with the same name). For
333non-group check boxes, use the {@link #checkbox checkbox} method instead.
334<p>
335Note: Checkboxes with no specific value are submitted with the value
336"on" (case-insensitive) by the browser (as seen in the second and
337third example below). Example:
338<blockquote><pre>
339
340<b><u>Good</u></b>:
341&lt;input type=checkbox name=<span style="color:green">foo</span> <u>value='<span style="color:blue">somevalue1</span>'</u> [=State.checkboxgroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"somevalue1"</span>)]&gt;
342&lt;input type=checkbox name=<span style="color:green">foo</span> <u>value='<span style="color:blue">somevalue2</span>'</u> [=State.checkboxgroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"somevalue2"</span>)]&gt;
343
344<b>Don't do this</b>:
345&lt;input type=checkbox name=<span style="color:green">foo</span> [=State.checkboxgroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"on"</span>)]&gt;
346&lt;input type=checkbox name=<span style="color:green">foo</span> [=State.checkboxgroup(req, "<span style="color:green">foo</span>", <span style="color:blue">"on"</span>)]&gt;
347</pre></blockquote>
348This method takes a <code>value</code> parameter that specifies the
349value of the check box in the checkbox group. <u>This method
350requires that each checkbox in the group have a specified and unique
351value attribute</u>. 
352<p>
353<b>Note</b>: For check boxes, the state maintainence value is the
354presence/absence of the <code>checked</code> attribute. The value
355returned by this method will be either an empty string or
356<code>checked</code>. This should not be assigned to the 'value'
357attribute (if present) of the checkbox. Rather, it should be a
358stand-alone attribute.
359*/
360public static final String checkboxgroup(
361  final HttpServletRequest req, final String name, final String value)
362  {
363  final String[] val = getParameterValues(req, name);
364  if (dbg) System.out.println(name+"="+Arrays.toString(val));
365  
366  if (val == null)
367    return "";
368  
369  /*
370  we cannot say:
371  
372    if (val[n].equals(value) || val[n].equalsIgnoreCase("on")) ...
373  
374  because a checkbox with no value - if checked - will also check all
375  other checkboxes in the group (since we get foo=on and we don't
376  know which checkbox to turn on in that case, since they are all 
377  called foo)
378  */
379  for (int n = 0; n < val.length; n++) 
380    {
381    if (val[n].equals(value)) {
382      return "checked";
383      }
384    }
385  
386  return "";
387  }
388
389
390/**
391Returns the value of the submitted hidden field or an empty string if no
392value was submitted (this can happen if the user hacked/removed this
393value manually).
394Example:
395<blockquote><pre>
396&lt;input type=hidden name=<span style="color:green">foo</span> value='[=State.hidden(req, "<span style="color:green">foo</span>")]'&gt;
397</pre></blockquote>
398*/
399public static final String hidden(final HttpServletRequest req, final String name)
400  {
401  return text(req, name);
402  }
403
404
405//<input type=image: get x,y co-ordinates of the click. not needed for form
406//maintainence
407//<input type=submit: not needed for form maintainence
408
409
410/**
411Returns the state of the submitted select. For select options, we
412need to maintain whichever option(s) were selected by the user. The
413presence/absence of the <code>selected</code> attribute maintains this
414state.
415<p>
416When maintaining states for select input types:
417<ol>
418<li>
419We know the select options on the server-side and write them
420out dynamically (or statically) on the server-side and 
421then send the page to the client. To maintain state, we 
422say:
423<blockquote>
424<pre>
425&lt;select name=<span style="color:green">sel1</span>&gt;
426&lt;option value=<span style="color:red">"1"</span> <span style="color:blue">[=State.select(req, <span style="color:green">"sel1"</span>, <span style="color:red">"1")</span>]</span>&gt; Option 1
427&lt;option           <span style="color:blue">[=State.select(req, <span style="color:green">"sel1"</span>, <span style="color:red">"Option 1"</span>)]</span>&gt; <span style="color:red">Option 1</span>
428&lt;/select&gt;
429</pre>
430</blockquote>
431
432<li>
433We query and create select options on the client-side using
434ajax/js/etc (for example, if the client chooses a country, we query
435a database and show the states for that country via another
436client-side select, created dynamically). Upon submission of this
437form, we still need to maintain the state of this option. To do
438this, we need to know, on the server side, the names of any new
439selects that are created on the client (and/or the names of
440initially empty selects that are re-populated on the client).
441Suppose, initially we have:
442
443<blockquote><pre>
444&lt;select name=<span style="color:green">states</span>&gt;
445&lt;/select&gt;
446</pre></blockquote>
447
448On the client, we then add:
449<blockquote><pre>
450&lt;select name=<span style="color:green">states</span>&gt;
451&lt;option&gt; <span style="color:red">Pennsylvania</span> (generated on client)
452&lt;/select&gt;
453</pre></blockquote>
454
455To maintain the state on the server for this option, we now have to 
456say on the server-side (upon form submission):
457<blockquote><pre>
458&lt;select name=<span style="color:green">states</span>&gt;
459   &lt;option <span style="color:blue">[=State.select(req, <span style="color:green">"states"</span>, </span><span style="color:red">"?"</span>)]&gt;<span style="color:red">Pennsylvania</span> (generated on client) 
460&lt;select>
461</pre></blockquote>
462
463But we don't know what value to put in <code><span style="color:red">"?"</span></code>
464on the server-side because <code><span style="color:red">Pennsylvania</span></code> was generated on the 
465client. So we can do any of the following:
466<ol style="list-style: square">
467<li>Rerun the query on the server, get the list of states again
468and say, for example something like:
469<blockquote>
470<pre>
471[[
472if (...options were added by by client...)
473    { //maintain state
474    List option_list == ...create new {@link SelectOption}'s here [possibly via a query]...
475    out.print(" &lt;select name=<span style="color:green">states</span>&gt; ");
476    for (int n = 0; n &lt; option_list.size(); n++) 
477        {
478        {@link SelectOption} opt = (SelectOption) option_list.get(n);
479        String html = opt.getHTML();
480        String val  = opt.getValue();
481        out.print(" &lt;option val=");
482        our.print(val);
483        if (<span style="color:blue">State.select(req, <span style="color:green">"states"</span>, val)</span>) {
484            out.print(" selected ");
485            }
486        out.print(" &gt; ");
487        out.print(html);
488        }
489    out.print(" &lt;/select&gt; ");
490    }
491]]
492</pre></blockquote>
493But this (kind of) defeats the purpose of using ajax because we have to 
494replicate the logic on the server. 
495</li>
496<li>
497Another solution is to write out the possible values to be shown to
498the client as a javascript array on the page. Then the client js can
499show the contents of a an array depending on user choice. This saves
500a ajax call to the database. If we store the results of the query in
501a cache or a static variable, then we don't have to re-run the query
502every time and can simply rewrite the array from the cached query
503results.
504</li>
505<li>
506Another option is that when the form is submitted to the server, it
507sets a hidden field with the submitted value of the select. Upon
508recieving the page, javascript on the browser re-runs the query on
509the client side again and then uses the hidden field to restore the
510state of the select option.
511</li>
512</ol>
513<p>These issue only arise when select elements are being modified
514on the client side via Javascript. 
515</li>
516</ol>
517
518@param  req       the HttpServletRequest 
519@param  selectName    the name of the select field
520@param  optionVal   the value attribute of the option. If the
521            option has no option attribute, the html
522            text associated with that option. Browsers
523            do not submit leading spaces in the html
524            text so, for example for: <code>
525            "&lt;option&gt;&nbsp;&nbsp;Option 1"</code>, the
526            value would be "Option 1"
527
528*/
529public static final String select(final HttpServletRequest req, 
530        final String selectName, final String optionVal)
531  {
532  final String[] val = getParameterValues(req, selectName);
533  if (dbg) System.out.println(selectName+"="+Arrays.toString(val));
534  
535  if (val == null)
536    return "";
537  
538  for (int n = 0; n < val.length; n++) {
539    if (dbg) System.out.println(val[n] + ";" + optionVal + ";" + (optionVal.equals(val[n]) ? "true/checked" : "false") );
540    if (optionVal.equals(val[n])) {
541      return "selected";
542      }
543    }
544  
545  return "";
546  }
547
548
549/**
550Convenience method that creates a list of select options that
551maintain form state. Example:
552<blockquote><pre>
553[[
554List list = (..create a list of {@link SelectOption}, possible from database lookup..)
555]]
556&lt;select name=foo&gt;
557[[ makeOptions(out, req, "foo", list); ]]
558&lt;/select&gt;
559</pre></blockquote>
560
561@param  out     destination where the options will be printed
562@param  req     the current HttpServletRequest
563@param  selectName  the name of the select field
564@param  optionList  a list of objects of type {@link SelectOption}
565*/
566public static void makeOptions(final PrintWriter out,
567 final HttpServletRequest req, 
568 final String selectName, final List optionList)
569  {
570  final int size = optionList.size();
571  for (int n = 0; n < size; n++) 
572    {
573    SelectOption opt = (SelectOption) optionList.get(n);
574    String html = HTMLUtil.quoteToEntity(opt.getHTML());
575    String val = opt.getValue();
576    String escaped_val = HTMLUtil.quoteToEntity(val);
577    out.print("<option val='");
578    out.print(escaped_val);
579    out.print("'");
580    out.print(opt.selected() ? " selected " : " ");
581    out.print(State.select(req, selectName, val));
582    out.print(">");
583    out.print(html);
584    if ( (n + 1) < size) {
585      out.print("\n");
586      }
587    }
588  }
589
590/**
591Convenience method that creates a list of select options that
592maintain form state. Returns the list of options as a string
593(rather than printing it to a printwriter as some other variants of
594this method do). Example:
595<blockquote><pre>
596[[
597List list = (..create a list of {@link SelectOption}, possible from database lookup..)
598]]
599&lt;select name=foo&gt;
600[= makeOptions(req, "foo", list); ]
601&lt;/select&gt;
602</pre></blockquote>
603
604@param  req     the current HttpServletRequest
605@param  selectName  the name of the select field
606@param  optionList  a list of objects of type {@link SelectOption}
607*/
608public static String makeOptions(final HttpServletRequest req, 
609 final String selectName, final List optionList)
610  {
611  final int size = optionList.size();
612  final StringBuilder buf = new StringBuilder(size * 32);
613  for (int n = 0; n < size; n++) 
614    {
615    SelectOption opt = (SelectOption) optionList.get(n);
616    String html = HTMLUtil.quoteToEntity(opt.getHTML());
617    String val = opt.getValue();
618    String escaped_val = HTMLUtil.quoteToEntity(val);
619    buf.append("<option val='");
620    buf.append(escaped_val);
621    buf.append("'");
622    buf.append(opt.selected() ? " selected " : " ");
623    buf.append(State.select(req, selectName, val));
624    buf.append(">");
625    buf.append(html);
626    if ( (n + 1) < size) {
627      buf.append("\n");
628      }
629    }
630  return buf.toString();
631  }
632
633
634
635
636/**
637Sets the value of the form element with the specified name. This
638method is useful to set initial values for the form. The initial
639values shown to the user can be different per user, for example,
640when the user is editing some data that already exists on the database.
641<p>
642This method can be invoked more than once. This is useful to set
643multiple values for the same field (for example, a select box
644with multiple selections or multiple checkboxes with the same name).
645<blockquote><pre>
646State.set(req, "checkbox1", "1.a");
647State.set(req, "checkbox1", "1.b");
648</pre></blockquote>
649*/
650public static final void set(
651 final HttpServletRequest req, final String name, final String value)
652  {
653  final Object obj = req.getAttribute(name);
654  
655  if (obj == null)  {  //obj not already present
656    req.setAttribute(name, value);
657    return;
658    }
659
660  //obj is present. could be a single string or string[] (list)
661  if (obj instanceof String) 
662    {
663    //defaults to size of 10 which is fine for us
664    final ArrayList list = new ArrayList();
665    
666    list.add(obj);   //save old single string value
667    list.add(value); //save new value
668    req.setAttribute(name, list);
669    }
670  else if (obj instanceof ArrayList) {
671    //append new string value to the list
672    ((ArrayList)obj).add(value);
673    }
674  else{ 
675    throw new IllegalArgumentException("Only strings can be added. You are trying to add: " + obj + "/" + obj.getClass().getName());
676    }
677  }
678
679private static final String cleared_key = "_fc.web.simpleforms.cleared";
680  
681/**
682The servlet api is written by retarded monkeys smoking crack....which 
683is why there is no way to modify/remove/change/clear 
684the parameters in the servlet request (the parameters map is read-only)
685..which leads to all sort of problems in html form processing.
686<p>
687This method changes the state of <b>all</b> fields such that methods
688that return state (text, textarea, radio etc) will act as if the
689form field had not been submitted by the user. However, any values
690set by the {@link set} method <i>after</i> invoking this method
691<i>will</i> be seen.
692<p>
693All this is really useful when form data has been saved and the
694user needs to be shown the same form, but with an empty (fresh)
695form state. So for example:
696<blockquote><pre>
697// At the top of a page
698String field_1 = req.getParameter("field_1");
699String field_2 = req.getParameter("field_2");
700
701Errors err =  validate_and_save(field1, field2);
702if (err == null) {
703    State.<span style="color:blue">clear</span>(req);  //req is the HttpServletRequest object
704    State.set("field_1", "enter a value"); //initial value for field_1
705    }
706
707&lt;form&gt;
708[[ if (err != null) { err.renderFormErrors(out); } ]]
709&lt;input type=text name=field_1 value='[=State.text(req, "field_1")]'&gt;
710&lt;input type=text name=field_2 value='[=State.text(req, "field_2")]'&gt;
711&lt;/form&gt;
712</pre></blockquote>
713Note: redirecting the client to the form page also has the same
714effect (of clearing request parameters), because that forces a 
715entirely new request object to be used on the server.
716*/
717public static final void clear(final HttpServletRequest req)
718  {
719  Enumeration e = req.getAttributeNames();
720  while (e.hasMoreElements()) {
721    req.removeAttribute((String)e.nextElement());
722    }
723    
724  req.setAttribute(cleared_key, "");
725  }
726  
727// ----------- escaped ----------------
728/**
729Convenience method that returns: <code>{@link 
730fc.util.HTMLUtil#quoteToEntity HTMLUtil.quoteToEntity}(text(req, name));</code>
731Useful for setting arbitrary values in the value="..." part 
732of a text field.
733*/
734public static final String escapedText(
735final HttpServletRequest req, final String name)
736  {
737  return HTMLUtil.quoteToEntity(text(req, name));
738  }
739
740/**
741Convenience method that returns: <code>{@link 
742fc.util.HTMLUtil#quoteToEntity(String) HTMLUtil.quoteToEntity}(password (req, name));</code>
743Useful for setting arbitrary values in the value="..." part 
744of a password field.
745*/
746public static final String escapedPassword(
747final HttpServletRequest req, final String name)
748  {
749  return HTMLUtil.quoteToEntity(password(req, name));
750  }
751
752
753/**
754Convenience method that returns: <code>{@link 
755fc.util.HTMLUtil#quoteToEntity(String) HTMLUtil.quoteToEntity}(hidden(req, name));</code>
756Useful for setting arbitrary values in the value="..." part 
757of a hidden field.
758*/
759public static final String escapedHidden(
760final HttpServletRequest req, final String name)
761  {
762  return HTMLUtil.quoteToEntity(hidden(req, name));
763  }
764
765
766private static final String disabled_key = "_fc.web.simpleforms.disabled";
767
768/**
769Convenience method that marks the specified field as disabled. This
770value can be later retrieved while rendering the form via the
771getDisabled or disabled methods.
772*/
773public static final void setDisabled(
774 final HttpServletRequest req, final String name)
775  {
776  Set set = (Set) req.getAttribute(disabled_key);
777  if (set == null) {
778    set = new HashSet();
779    req.setAttribute(disabled_key, set);
780    }
781  set.add(name);
782  }
783
784/**
785Returns true if the specified field was marked as disabled. 
786Example usage:
787<blockquote><pre>
788[= State.getDisabled(req, "somefield") ? "disabled" : ""]
789</pre></blockquote>
790*/
791public static final boolean getDisabled(
792 final HttpServletRequest req, final String name)
793  {
794  Set set = (Set) req.getAttribute(disabled_key);
795  if (set == null) {
796    return false;
797    }
798  return set.contains(name);
799  }
800
801/**
802Convenience method. Instead of saying:
803Example usage:
804<blockquote><pre>
805[= State.getDisabled(req, "somefield") ? "disabled" : ""]
806</pre></blockquote>
807this method allows one to say:
808<blockquote><pre>
809[= State.disabled(req, "somefield")]
810</pre></blockquote>
811*/
812public static final String disabled(
813 final HttpServletRequest req, final String name)
814  {
815  if (getDisabled(req, name))
816    return "disabled";
817  else 
818    return "";
819  }
820
821} //~class State