1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.struts.taglib.html;
22
23 import org.apache.struts.Globals;
24 import org.apache.struts.action.ActionForm;
25 import org.apache.struts.action.ActionMapping;
26 import org.apache.struts.action.ActionServlet;
27 import org.apache.struts.config.ActionConfig;
28 import org.apache.struts.config.FormBeanConfig;
29 import org.apache.struts.config.ModuleConfig;
30 import org.apache.struts.taglib.TagUtils;
31 import org.apache.struts.util.MessageResources;
32 import org.apache.struts.util.RequestUtils;
33
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36 import javax.servlet.http.HttpSession;
37 import javax.servlet.jsp.JspException;
38 import javax.servlet.jsp.JspWriter;
39 import javax.servlet.jsp.PageContext;
40 import javax.servlet.jsp.tagext.TagSupport;
41
42 import java.io.IOException;
43
44 /**
45 * Custom tag that represents an input form, associated with a bean whose
46 * properties correspond to the various fields of the form.
47 *
48 * @version $Rev: 479633 $ $Date: 2006-11-27 08:25:35 -0600 (Mon, 27 Nov 2006) $
49 */
50 public class FormTag extends TagSupport {
51 /**
52 * The line ending string.
53 */
54 protected static String lineEnd = System.getProperty("line.separator");
55
56 /**
57 * The message resources for this package.
58 */
59 protected static MessageResources messages =
60 MessageResources.getMessageResources(Constants.Package
61 + ".LocalStrings");
62
63
64
65 /**
66 * The action URL to which this form should be submitted, if any.
67 */
68 protected String action = null;
69
70 /**
71 * A postback action URL to which this form should be submitted, if any.
72 */
73 private String postbackAction = null;
74
75 /**
76 * The module configuration for our module.
77 */
78 protected ModuleConfig moduleConfig = null;
79
80 /**
81 * The content encoding to be used on a POST submit.
82 */
83 protected String enctype = null;
84
85 /**
86 * The name of the field to receive focus, if any.
87 */
88 protected String focus = null;
89
90 /**
91 * The index in the focus field array to receive focus. This only applies
92 * if the field given in the focus attribute is actually an array of
93 * fields. This allows a specific field in a radio button array to
94 * receive focus while still allowing indexed field names like
95 * "myRadioButtonField[1]" to be passed in the focus attribute.
96 *
97 * @since Struts 1.1
98 */
99 protected String focusIndex = null;
100
101 /**
102 * The ActionMapping defining where we will be submitting this form
103 */
104 protected ActionMapping mapping = null;
105
106 /**
107 * The request method used when submitting this form.
108 */
109 protected String method = null;
110
111 /**
112 * The onReset event script.
113 */
114 protected String onreset = null;
115
116 /**
117 * The onSubmit event script.
118 */
119 protected String onsubmit = null;
120
121 /**
122 * Include language attribute in the focus script's <script>
123 * element. This property is ignored in XHTML mode.
124 *
125 * @since Struts 1.2
126 */
127 protected boolean scriptLanguage = true;
128
129 /**
130 * The ActionServlet instance we are associated with (so that we can
131 * initialize the <code>servlet</code> property on any form bean that we
132 * create).
133 */
134 protected ActionServlet servlet = null;
135
136 /**
137 * The style attribute associated with this tag.
138 */
139 protected String style = null;
140
141 /**
142 * The style class associated with this tag.
143 */
144 protected String styleClass = null;
145
146 /**
147 * The identifier associated with this tag.
148 */
149 protected String styleId = null;
150
151 /**
152 * The window target.
153 */
154 protected String target = null;
155
156 /**
157 * The name of the form bean to (create and) use. This is either the same
158 * as the 'name' attribute, if that was specified, or is obtained from the
159 * associated <code>ActionMapping</code> otherwise.
160 */
161 protected String beanName = null;
162
163 /**
164 * The scope of the form bean to (create and) use. This is either the same
165 * as the 'scope' attribute, if that was specified, or is obtained from
166 * the associated <code>ActionMapping</code> otherwise.
167 */
168 protected String beanScope = null;
169
170 /**
171 * The type of the form bean to (create and) use. This is either the same
172 * as the 'type' attribute, if that was specified, or is obtained from the
173 * associated <code>ActionMapping</code> otherwise.
174 */
175 protected String beanType = null;
176
177 /**
178 * The list of character encodings for input data that the server should
179 * accept.
180 */
181 protected String acceptCharset = null;
182
183 /**
184 * Controls whether child controls should be 'disabled'.
185 */
186 private boolean disabled = false;
187
188 /**
189 * Controls whether child controls should be 'readonly'.
190 */
191 protected boolean readonly = false;
192
193 /**
194 * The language code of this element.
195 */
196 private String lang = null;
197
198 /**
199 * The direction for weak/neutral text of this element.
200 */
201 private String dir = null;
202
203
204
205 /**
206 * Return the name of the form bean corresponding to this tag. There is no
207 * corresponding setter method; this method exists so that the nested tag
208 * classes can obtain the actual bean name derived from other attributes
209 * of the tag.
210 */
211 public String getBeanName() {
212 return beanName;
213 }
214
215 /**
216 * Return the action URL to which this form should be submitted.
217 */
218 public String getAction() {
219 return (this.action);
220 }
221
222 /**
223 * Set the action URL to which this form should be submitted.
224 *
225 * @param action The new action URL
226 */
227 public void setAction(String action) {
228 this.action = action;
229 }
230
231 /**
232 * Return the content encoding used when submitting this form.
233 */
234 public String getEnctype() {
235 return (this.enctype);
236 }
237
238 /**
239 * Set the content encoding used when submitting this form.
240 *
241 * @param enctype The new content encoding
242 */
243 public void setEnctype(String enctype) {
244 this.enctype = enctype;
245 }
246
247 /**
248 * Return the focus field name for this form.
249 */
250 public String getFocus() {
251 return (this.focus);
252 }
253
254 /**
255 * Set the focus field name for this form.
256 *
257 * @param focus The new focus field name
258 */
259 public void setFocus(String focus) {
260 this.focus = focus;
261 }
262
263 /**
264 * Return the request method used when submitting this form.
265 */
266 public String getMethod() {
267 return (this.method);
268 }
269
270 /**
271 * Set the request method used when submitting this form.
272 *
273 * @param method The new request method
274 */
275 public void setMethod(String method) {
276 this.method = method;
277 }
278
279 /**
280 * Return the onReset event script.
281 */
282 public String getOnreset() {
283 return (this.onreset);
284 }
285
286 /**
287 * Set the onReset event script.
288 *
289 * @param onReset The new event script
290 */
291 public void setOnreset(String onReset) {
292 this.onreset = onReset;
293 }
294
295 /**
296 * Return the onSubmit event script.
297 */
298 public String getOnsubmit() {
299 return (this.onsubmit);
300 }
301
302 /**
303 * Set the onSubmit event script.
304 *
305 * @param onSubmit The new event script
306 */
307 public void setOnsubmit(String onSubmit) {
308 this.onsubmit = onSubmit;
309 }
310
311 /**
312 * Return the style attribute for this tag.
313 */
314 public String getStyle() {
315 return (this.style);
316 }
317
318 /**
319 * Set the style attribute for this tag.
320 *
321 * @param style The new style attribute
322 */
323 public void setStyle(String style) {
324 this.style = style;
325 }
326
327 /**
328 * Return the style class for this tag.
329 */
330 public String getStyleClass() {
331 return (this.styleClass);
332 }
333
334 /**
335 * Set the style class for this tag.
336 *
337 * @param styleClass The new style class
338 */
339 public void setStyleClass(String styleClass) {
340 this.styleClass = styleClass;
341 }
342
343 /**
344 * Return the style identifier for this tag.
345 */
346 public String getStyleId() {
347 return (this.styleId);
348 }
349
350 /**
351 * Set the style identifier for this tag.
352 *
353 * @param styleId The new style identifier
354 */
355 public void setStyleId(String styleId) {
356 this.styleId = styleId;
357 }
358
359 /**
360 * Return the window target.
361 */
362 public String getTarget() {
363 return (this.target);
364 }
365
366 /**
367 * Set the window target.
368 *
369 * @param target The new window target
370 */
371 public void setTarget(String target) {
372 this.target = target;
373 }
374
375 /**
376 * Return the list of character encodings accepted.
377 */
378 public String getAcceptCharset() {
379 return acceptCharset;
380 }
381
382 /**
383 * Set the list of character encodings accepted.
384 *
385 * @param acceptCharset The list of character encodings
386 */
387 public void setAcceptCharset(String acceptCharset) {
388 this.acceptCharset = acceptCharset;
389 }
390
391 /**
392 * Sets the disabled event handler.
393 */
394 public void setDisabled(boolean disabled) {
395 this.disabled = disabled;
396 }
397
398 /**
399 * Returns the disabled event handler.
400 */
401 public boolean isDisabled() {
402 return disabled;
403 }
404
405 /**
406 * Sets the readonly event handler.
407 */
408 public void setReadonly(boolean readonly) {
409 this.readonly = readonly;
410 }
411
412 /**
413 * Returns the readonly event handler.
414 */
415 public boolean isReadonly() {
416 return readonly;
417 }
418
419 /**
420 * Returns the language code of this element.
421 *
422 * @since Struts 1.3.6
423 */
424 public String getLang() {
425 return this.lang;
426 }
427
428 /**
429 * Sets the language code of this element.
430 *
431 * @since Struts 1.3.6
432 */
433 public void setLang(String lang) {
434 this.lang = lang;
435 }
436
437 /**
438 * Returns the direction for weak/neutral text this element.
439 *
440 * @since Struts 1.3.6
441 */
442 public String getDir() {
443 return this.dir;
444 }
445
446 /**
447 * Sets the direction for weak/neutral text of this element.
448 *
449 * @since Struts 1.3.6
450 */
451 public void setDir(String dir) {
452 this.dir = dir;
453 }
454
455
456
457 /**
458 * Render the beginning of this form.
459 *
460 * @throws JspException if a JSP exception has occurred
461 */
462 public int doStartTag() throws JspException {
463
464 postbackAction = null;
465
466
467 this.lookup();
468
469
470 StringBuffer results = new StringBuffer();
471
472 results.append(this.renderFormStartElement());
473
474 results.append(this.renderToken());
475
476 TagUtils.getInstance().write(pageContext, results.toString());
477
478
479 pageContext.setAttribute(Constants.FORM_KEY, this,
480 PageContext.REQUEST_SCOPE);
481
482 this.initFormBean();
483
484 return (EVAL_BODY_INCLUDE);
485 }
486
487 /**
488 * Locate or create the bean associated with our form.
489 *
490 * @throws JspException
491 * @since Struts 1.1
492 */
493 protected void initFormBean()
494 throws JspException {
495 int scope = PageContext.SESSION_SCOPE;
496
497 if ("request".equalsIgnoreCase(beanScope)) {
498 scope = PageContext.REQUEST_SCOPE;
499 }
500
501 Object bean = pageContext.getAttribute(beanName, scope);
502
503 if (bean == null) {
504
505 bean =
506 RequestUtils.createActionForm((HttpServletRequest) pageContext
507 .getRequest(), mapping, moduleConfig, servlet);
508
509 if (bean instanceof ActionForm) {
510 ((ActionForm) bean).reset(mapping,
511 (HttpServletRequest) pageContext.getRequest());
512 }
513
514 if (bean == null) {
515 throw new JspException(messages.getMessage("formTag.create",
516 beanType));
517 }
518
519 pageContext.setAttribute(beanName, bean, scope);
520 }
521
522 pageContext.setAttribute(Constants.BEAN_KEY, bean,
523 PageContext.REQUEST_SCOPE);
524 }
525
526 /**
527 * Generates the opening <code><form></code> element with
528 * appropriate attributes.
529 *
530 * @since Struts 1.1
531 */
532 protected String renderFormStartElement()
533 throws JspException {
534 StringBuffer results = new StringBuffer("<form");
535
536
537 renderName(results);
538
539 renderAttribute(results, "method",
540 (getMethod() == null) ? "post" : getMethod());
541 renderAction(results);
542 renderAttribute(results, "accept-charset", getAcceptCharset());
543 renderAttribute(results, "class", getStyleClass());
544 renderAttribute(results, "dir", getDir());
545 renderAttribute(results, "enctype", getEnctype());
546 renderAttribute(results, "lang", getLang());
547 renderAttribute(results, "onreset", getOnreset());
548 renderAttribute(results, "onsubmit", getOnsubmit());
549 renderAttribute(results, "style", getStyle());
550 renderAttribute(results, "target", getTarget());
551
552
553 renderOtherAttributes(results);
554
555 results.append(">");
556
557 return results.toString();
558 }
559
560 /**
561 * Renders the name of the form. If XHTML is set to true, the name will
562 * be rendered as an 'id' attribute, otherwise as a 'name' attribute.
563 */
564 protected void renderName(StringBuffer results)
565 throws JspException {
566 if (this.isXhtml()) {
567 if (getStyleId() == null) {
568 renderAttribute(results, "id", beanName);
569 } else {
570 throw new JspException(messages.getMessage("formTag.ignoredId"));
571 }
572 } else {
573 renderAttribute(results, "name", beanName);
574 renderAttribute(results, "id", getStyleId());
575 }
576 }
577
578 /**
579 * Renders the action attribute
580 */
581 protected void renderAction(StringBuffer results) {
582 String calcAction = (this.action == null ? postbackAction : this.action);
583 HttpServletResponse response =
584 (HttpServletResponse) this.pageContext.getResponse();
585
586 results.append(" action=\"");
587 results.append(response.encodeURL(
588 TagUtils.getInstance().getActionMappingURL(calcAction,
589 this.pageContext)));
590
591 results.append("\"");
592 }
593
594 /**
595 * 'Hook' to enable this tag to be extended and additional attributes
596 * added.
597 */
598 protected void renderOtherAttributes(StringBuffer results) {
599 }
600
601 /**
602 * Generates a hidden input field with token information, if any. The
603 * field is added within a div element for HTML 4.01 Strict compliance.
604 *
605 * @return A hidden input field containing the token.
606 * @since Struts 1.1
607 */
608 protected String renderToken() {
609 StringBuffer results = new StringBuffer();
610 HttpSession session = pageContext.getSession();
611
612 if (session != null) {
613 String token =
614 (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
615
616 if (token != null) {
617 results.append("<div><input type=\"hidden\" name=\"");
618 results.append(Constants.TOKEN_KEY);
619 results.append("\" value=\"");
620 results.append(token);
621
622 if (this.isXhtml()) {
623 results.append("\" />");
624 } else {
625 results.append("\">");
626 }
627
628 results.append("</div>");
629 }
630 }
631
632 return results.toString();
633 }
634
635 /**
636 * Renders attribute="value" if not null
637 */
638 protected void renderAttribute(StringBuffer results, String attribute,
639 String value) {
640 if (value != null) {
641 results.append(" ");
642 results.append(attribute);
643 results.append("=\"");
644 results.append(value);
645 results.append("\"");
646 }
647 }
648
649 /**
650 * Render the end of this form.
651 *
652 * @throws JspException if a JSP exception has occurred
653 */
654 public int doEndTag() throws JspException {
655
656 pageContext.removeAttribute(Constants.BEAN_KEY,
657 PageContext.REQUEST_SCOPE);
658 pageContext.removeAttribute(Constants.FORM_KEY,
659 PageContext.REQUEST_SCOPE);
660
661
662 StringBuffer results = new StringBuffer("</form>");
663
664
665 if (this.focus != null) {
666 results.append(this.renderFocusJavascript());
667 }
668
669
670 JspWriter writer = pageContext.getOut();
671
672 try {
673 writer.print(results.toString());
674 } catch (IOException e) {
675 throw new JspException(messages.getMessage("common.io", e.toString()));
676 }
677
678 postbackAction = null;
679
680
681 return (EVAL_PAGE);
682 }
683
684 /**
685 * Generates javascript to set the initial focus to the form element given
686 * in the tag's "focus" attribute.
687 *
688 * @since Struts 1.1
689 */
690 protected String renderFocusJavascript() {
691 StringBuffer results = new StringBuffer();
692
693 results.append(lineEnd);
694 results.append("<script type=\"text/javascript\"");
695
696 if (!this.isXhtml() && this.scriptLanguage) {
697 results.append(" language=\"JavaScript\"");
698 }
699
700 results.append(">");
701 results.append(lineEnd);
702
703
704 if (!this.isXhtml()) {
705 results.append(" <!--");
706 results.append(lineEnd);
707 }
708
709
710
711 StringBuffer focusControl = new StringBuffer("document.forms[\"");
712
713 focusControl.append(beanName);
714 focusControl.append("\"].elements[\"");
715 focusControl.append(this.focus);
716 focusControl.append("\"]");
717
718 results.append(" var focusControl = ");
719 results.append(focusControl.toString());
720 results.append(";");
721 results.append(lineEnd);
722 results.append(lineEnd);
723
724 results.append(" if (focusControl.type != \"hidden\" && ");
725 results.append("!focusControl.disabled && ");
726 results.append("focusControl.style.display != \"none\") {");
727 results.append(lineEnd);
728
729
730 String index = "";
731
732 if (this.focusIndex != null) {
733 StringBuffer sb = new StringBuffer("[");
734
735 sb.append(this.focusIndex);
736 sb.append("]");
737 index = sb.toString();
738 }
739
740 results.append(" focusControl");
741 results.append(index);
742 results.append(".focus();");
743 results.append(lineEnd);
744
745 results.append(" }");
746 results.append(lineEnd);
747
748 if (!this.isXhtml()) {
749 results.append(" // -->");
750 results.append(lineEnd);
751 }
752
753 results.append("</script>");
754 results.append(lineEnd);
755
756 return results.toString();
757 }
758
759 /**
760 * Release any acquired resources.
761 */
762 public void release() {
763 super.release();
764 action = null;
765 moduleConfig = null;
766 enctype = null;
767 dir = null;
768 disabled = false;
769 focus = null;
770 focusIndex = null;
771 lang = null;
772 mapping = null;
773 method = null;
774 onreset = null;
775 onsubmit = null;
776 readonly = false;
777 servlet = null;
778 style = null;
779 styleClass = null;
780 styleId = null;
781 target = null;
782 acceptCharset = null;
783 }
784
785
786
787 /**
788 * Look up values for the <code>name</code>, <code>scope</code>, and
789 * <code>type</code> properties if necessary.
790 *
791 * @throws JspException if a required value cannot be looked up
792 */
793 protected void lookup() throws JspException {
794
795
796 moduleConfig = TagUtils.getInstance().getModuleConfig(pageContext);
797
798 if (moduleConfig == null) {
799 JspException e =
800 new JspException(messages.getMessage("formTag.collections"));
801
802 pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
803 PageContext.REQUEST_SCOPE);
804 throw e;
805 }
806
807 String calcAction = this.action;
808
809
810 if (this.action == null) {
811 HttpServletRequest request =
812 (HttpServletRequest) pageContext.getRequest();
813 postbackAction =
814 (String) request.getAttribute(Globals.ORIGINAL_URI_KEY);
815
816 String prefix = moduleConfig.getPrefix();
817 if (postbackAction != null && prefix.length() > 0 && postbackAction.startsWith(prefix)) {
818 postbackAction = postbackAction.substring(prefix.length());
819 }
820 calcAction = postbackAction;
821 } else {
822
823 ActionConfig actionConfig = moduleConfig.findActionConfigId(this.action);
824 if (actionConfig != null) {
825 this.action = actionConfig.getPath();
826 calcAction = this.action;
827 }
828 }
829
830 servlet =
831 (ActionServlet) pageContext.getServletContext().getAttribute(Globals.ACTION_SERVLET_KEY);
832
833
834 String mappingName =
835 TagUtils.getInstance().getActionMappingName(calcAction);
836
837 mapping = (ActionMapping) moduleConfig.findActionConfig(mappingName);
838
839 if (mapping == null) {
840 JspException e =
841 new JspException(messages.getMessage("formTag.mapping",
842 mappingName));
843
844 pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
845 PageContext.REQUEST_SCOPE);
846 throw e;
847 }
848
849
850 FormBeanConfig formBeanConfig =
851 moduleConfig.findFormBeanConfig(mapping.getName());
852
853 if (formBeanConfig == null) {
854 JspException e = null;
855
856 if (mapping.getName() == null) {
857 e = new JspException(messages.getMessage("formTag.name", calcAction));
858 } else {
859 e = new JspException(messages.getMessage("formTag.formBean",
860 mapping.getName(), calcAction));
861 }
862
863 pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
864 PageContext.REQUEST_SCOPE);
865 throw e;
866 }
867
868
869 beanName = mapping.getAttribute();
870 beanScope = mapping.getScope();
871 beanType = formBeanConfig.getType();
872 }
873
874 /**
875 * Returns true if this tag should render as xhtml.
876 */
877 private boolean isXhtml() {
878 return TagUtils.getInstance().isXhtml(this.pageContext);
879 }
880
881 /**
882 * Returns the focusIndex.
883 *
884 * @return String
885 */
886 public String getFocusIndex() {
887 return focusIndex;
888 }
889
890 /**
891 * Sets the focusIndex.
892 *
893 * @param focusIndex The focusIndex to set
894 */
895 public void setFocusIndex(String focusIndex) {
896 this.focusIndex = focusIndex;
897 }
898
899 /**
900 * Gets whether or not the focus script's <script> element will
901 * include the language attribute.
902 *
903 * @return true if language attribute will be included.
904 * @since Struts 1.2
905 */
906 public boolean getScriptLanguage() {
907 return this.scriptLanguage;
908 }
909
910 /**
911 * Sets whether or not the focus script's <script> element will
912 * include the language attribute.
913 *
914 * @since Struts 1.2
915 */
916 public void setScriptLanguage(boolean scriptLanguage) {
917 this.scriptLanguage = scriptLanguage;
918 }
919 }