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