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