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<input name=<font color=green>foo</font> type='text' value='<font color=blue>[= State.text(req, "<font color=green>foo</font>")]'</font>> 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<input name=<font color=green>foo</font> type='text' value='<font color=blue>[= State.text(req, "<font color=green>foo</font>")]'</font>> 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<input type=text name=<font color=green>foo</font> value='[=State.text(req, "<font color=green>foo</font>")]'> 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<textarea name=<font color=green>foo</font>> 152[=State.textarea(req, "<font color=green>foo</font>")] 153</textarea> 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<input type=password name=<font color=green>foo</font> value='[=State.password(req, "<font color=green>foo</font>")]'> 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<input type=radio name=<font color=green>foo</font> value='somevalue' [=State.radio(req, "<font color=green>foo</font>")]> 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<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>)]> 239<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>)]> 240 241<b>Don't do this</b> 242<input type=radio name=<font color=green>foo</font> [=State.radiogroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]> 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<input type=checkbox name=<font color=green>foo</font> value='somevalue' [=State.checkbox(req, "<font color=green>foo</font>")]> 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<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>)]> 345<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>)]> 346 347<b>Don't do this</b>: 348<input type=checkbox name=<font color=green>foo</font> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]> 349<input type=checkbox name=<font color=green>foo</font> [=State.checkboxgroup(req, "<font color=green>foo</font>", <font color=blue>"on"</font>)]> 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<input type=hidden name=<font color=green>foo</font> value='[=State.hidden(req, "<font color=green>foo</font>")]'> 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<select name=<font color=green>sel1</font>> 429<option value=<font color=red>"1"</font> <font color=blue>[=State.select(req, <font color=green>"sel1"</font>, <font color=red>"1")</font>]</font>> Option 1 430<option <font color=blue>[=State.select(req, <font color=green>"sel1"</font>, <font color=red>"Option 1"</font>)]</font>> <font color=red>Option 1</font> 431</select> 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<select name=<font color=green>states</font>> 448</select> 449</pre></blockquote> 450 451On the client, we then add: 452<blockquote><pre> 453<select name=<font color=green>states</font>> 454<option> <font color=red>Pennsylvania</font> (generated on client) 455</select> 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<select name=<font color=green>states</font>> 462 <option <font color=blue>[=State.select(req, <font color=green>"states"</font>, </font><font color=red>"?"</font>)]><font color=red>Pennsylvania</font> (generated on client) 463<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(" <select name=<font color=green>states</font>> "); 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(" <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(" > "); 491 out.print(html); 492 } 493 out.print(" </select> "); 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 "<option> 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<select name=foo> 561[[ makeOptions(out, req, "foo", list); ]] 562</select> 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<select name=foo> 604[= makeOptions(req, "foo", list); ] 605</select> 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<form> 712[[ if (err != null) { err.renderFormErrors(out); } ]] 713<input type=text name=field_1 value='[=State.text(req, "field_1")]'> 714<input type=text name=field_2 value='[=State.text(req, "field_2")]'> 715</form> 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