View Javadoc

1   /*
2    * $Id: JavascriptValidatorTag.java 421129 2006-07-12 05:13:54Z wsmoak $
3    *
4    * Copyright 2001-2006 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  package org.apache.struts.taglib.html;
19  
20  import org.apache.commons.validator.Field;
21  import org.apache.commons.validator.Form;
22  import org.apache.commons.validator.ValidatorAction;
23  import org.apache.commons.validator.ValidatorResources;
24  import org.apache.commons.validator.Var;
25  import org.apache.struts.Globals;
26  import org.apache.struts.action.ActionMapping;
27  import org.apache.struts.config.ModuleConfig;
28  import org.apache.struts.taglib.TagUtils;
29  import org.apache.struts.util.MessageResources;
30  import org.apache.struts.validator.Resources;
31  import org.apache.struts.validator.ValidatorPlugIn;
32  
33  import javax.servlet.ServletContext;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.jsp.JspException;
36  import javax.servlet.jsp.JspWriter;
37  import javax.servlet.jsp.PageContext;
38  import javax.servlet.jsp.tagext.BodyTagSupport;
39  
40  import java.io.IOException;
41  
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.Comparator;
45  import java.util.Iterator;
46  import java.util.List;
47  import java.util.Locale;
48  import java.util.Map;
49  import java.util.StringTokenizer;
50  
51  /***
52   * Custom tag that generates JavaScript for client side validation based on
53   * the validation rules loaded by the <code>ValidatorPlugIn</code> defined in
54   * the struts-config.xml file.
55   *
56   * @version $Rev: 421129 $ $Date: 2006-07-11 22:13:54 -0700 (Tue, 11 Jul 2006) $
57   * @since Struts 1.1
58   */
59  public class JavascriptValidatorTag extends BodyTagSupport {
60      /***
61       * A Comparator to use when sorting ValidatorAction objects.
62       */
63      private static final Comparator actionComparator =
64          new Comparator() {
65              public int compare(Object o1, Object o2) {
66                  ValidatorAction va1 = (ValidatorAction) o1;
67                  ValidatorAction va2 = (ValidatorAction) o2;
68  
69                  if (((va1.getDepends() == null)
70                      || (va1.getDepends().length() == 0))
71                      && ((va2.getDepends() == null)
72                      || (va2.getDepends().length() == 0))) {
73                      return 0;
74                  } else if (((va1.getDepends() != null)
75                      && (va1.getDepends().length() > 0))
76                      && ((va2.getDepends() == null)
77                      || (va2.getDepends().length() == 0))) {
78                      return 1;
79                  } else if (((va1.getDepends() == null)
80                      || (va1.getDepends().length() == 0))
81                      && ((va2.getDepends() != null)
82                      && (va2.getDepends().length() > 0))) {
83                      return -1;
84                  } else {
85                      return va1.getDependencyList().size()
86                      - va2.getDependencyList().size();
87                  }
88              }
89          };
90  
91      /***
92       * The start of the HTML comment hiding JavaScript from old browsers.
93       *
94       * @since Struts 1.2
95       */
96      protected static final String HTML_BEGIN_COMMENT = "\n<!-- Begin \n";
97  
98      /***
99       * The end of the HTML comment hiding JavaScript from old browsers.
100      *
101      * @since Struts 1.2
102      */
103     protected static final String HTML_END_COMMENT = "//End --> \n";
104 
105     /***
106      * The line ending string.
107      */
108     protected static String lineEnd = System.getProperty("line.separator");
109 
110     // ----------------------------------------------------------- Properties
111 
112     /***
113      * The servlet context attribute key for our resources.
114      */
115     protected String bundle = Globals.MESSAGES_KEY;
116 
117     /***
118      * The name of the form that corresponds with the action name in
119      * struts-config.xml. Specifying a form name places a &lt;script&gt;
120      * &lt;/script&gt; around the javascript.
121      */
122     protected String formName = null;
123 
124     /***
125      * formName is used for both Javascript and non-javascript validations.
126      * For the javascript validations, there is the possibility that we will
127      * be rewriting the formName (if it is a ValidatorActionForm instead of
128      * just a ValidatorForm) so we need another variable to hold the formName
129      * just for javascript usage.
130      */
131     protected String jsFormName = null;
132 
133     /***
134      * The current page number of a multi-part form. Only valid when the
135      * formName attribute is set.
136      */
137     protected int page = 0;
138 
139     /***
140      * This will be used as is for the JavaScript validation method name if it
141      * has a value.  This is the method name of the main JavaScript method
142      * that the form calls to perform validations.
143      */
144     protected String methodName = null;
145 
146     /***
147      * Include language attribute in the &lt;script&gt; element.  This
148      * property is ignored in XHTML mode.
149      *
150      * @since Struts 1.2
151      */
152     protected boolean scriptLanguage = true;
153 
154     /***
155      * The static JavaScript methods will only be printed if this is set to
156      * "true".
157      */
158     protected String staticJavascript = "true";
159 
160     /***
161      * The dynamic JavaScript objects will only be generated if this is set to
162      * "true".
163      */
164     protected String dynamicJavascript = "true";
165 
166     /***
167      * The src attribute for html script element (used to include an external
168      * script resource). The src attribute is only recognized when the
169      * formName attribute is specified.
170      */
171     protected String src = null;
172 
173     /***
174      * The JavaScript methods will enclosed with html comments if this is set
175      * to "true".
176      */
177     protected String htmlComment = "true";
178 
179     /***
180      * Hide JavaScript methods in a CDATA section for XHTML when "true".
181      */
182     protected String cdata = "true";
183 
184     /***
185      * Gets the key (form name) that will be used to retrieve a set of
186      * validation rules to be performed on the bean passed in for validation.
187      */
188     public String getFormName() {
189         return formName;
190     }
191 
192     /***
193      * Sets the key (form name) that will be used to retrieve a set of
194      * validation rules to be performed on the bean passed in for validation.
195      * Specifying a form name places a &lt;script&gt; &lt;/script&gt; tag
196      * around the javascript.
197      */
198     public void setFormName(String formName) {
199         this.formName = formName;
200     }
201 
202     /***
203      * @return Returns the jsFormName.
204      */
205     public String getJsFormName() {
206         return jsFormName;
207     }
208 
209     /***
210      * @param jsFormName The jsFormName to set.
211      */
212     public void setJsFormName(String jsFormName) {
213         this.jsFormName = jsFormName;
214     }
215 
216     /***
217      * Gets the current page number of a multi-part form. Only field
218      * validations with a matching page numer will be generated that match the
219      * current page number. Only valid when the formName attribute is set.
220      */
221     public int getPage() {
222         return page;
223     }
224 
225     /***
226      * Sets the current page number of a multi-part form. Only field
227      * validations with a matching page numer will be generated that match the
228      * current page number. Only valid when the formName attribute is set.
229      */
230     public void setPage(int page) {
231         this.page = page;
232     }
233 
234     /***
235      * Gets the method name that will be used for the Javascript validation
236      * method name if it has a value.  This overrides the auto-generated
237      * method name based on the key (form name) passed in.
238      */
239     public String getMethod() {
240         return methodName;
241     }
242 
243     /***
244      * Sets the method name that will be used for the Javascript validation
245      * method name if it has a value.  This overrides the auto-generated
246      * method name based on the key (form name) passed in.
247      */
248     public void setMethod(String methodName) {
249         this.methodName = methodName;
250     }
251 
252     /***
253      * Gets whether or not to generate the static JavaScript.  If this is set
254      * to 'true', which is the default, the static JavaScript will be
255      * generated.
256      */
257     public String getStaticJavascript() {
258         return staticJavascript;
259     }
260 
261     /***
262      * Sets whether or not to generate the static JavaScript.  If this is set
263      * to 'true', which is the default, the static JavaScript will be
264      * generated.
265      */
266     public void setStaticJavascript(String staticJavascript) {
267         this.staticJavascript = staticJavascript;
268     }
269 
270     /***
271      * Gets whether or not to generate the dynamic JavaScript.  If this is set
272      * to 'true', which is the default, the dynamic JavaScript will be
273      * generated.
274      */
275     public String getDynamicJavascript() {
276         return dynamicJavascript;
277     }
278 
279     /***
280      * Sets whether or not to generate the dynamic JavaScript.  If this is set
281      * to 'true', which is the default, the dynamic JavaScript will be
282      * generated.
283      */
284     public void setDynamicJavascript(String dynamicJavascript) {
285         this.dynamicJavascript = dynamicJavascript;
286     }
287 
288     /***
289      * Gets whether or not to delimit the JavaScript with html comments.  If
290      * this is set to 'true', which is the default, the htmlComment will be
291      * surround the JavaScript.
292      */
293     public String getHtmlComment() {
294         return htmlComment;
295     }
296 
297     /***
298      * Sets whether or not to delimit the JavaScript with html comments.  If
299      * this is set to 'true', which is the default, the htmlComment will be
300      * surround the JavaScript.
301      */
302     public void setHtmlComment(String htmlComment) {
303         this.htmlComment = htmlComment;
304     }
305 
306     /***
307      * Gets the src attribute's value when defining the html script element.
308      */
309     public String getSrc() {
310         return src;
311     }
312 
313     /***
314      * Sets the src attribute's value when defining the html script element.
315      * The src attribute is only recognized when the formName attribute is
316      * specified.
317      */
318     public void setSrc(String src) {
319         this.src = src;
320     }
321 
322     /***
323      * Sets the servlet context attribute key for our resources.
324      */
325     public String getBundle() {
326         return bundle;
327     }
328 
329     /***
330      * Gets the servlet context attribute key for our resources.
331      */
332     public void setBundle(String bundle) {
333         this.bundle = bundle;
334     }
335 
336     /***
337      * Render the JavaScript for to perform validations based on the form
338      * name.
339      *
340      * @throws JspException if a JSP exception has occurred
341      */
342     public int doStartTag() throws JspException {
343         JspWriter writer = pageContext.getOut();
344 
345         try {
346             writer.print(this.renderJavascript());
347         } catch (IOException e) {
348             throw new JspException(e.getMessage());
349         }
350 
351         return EVAL_BODY_TAG;
352     }
353 
354     /***
355      * Returns fully rendered JavaScript.
356      *
357      * @since Struts 1.2
358      */
359     protected String renderJavascript()
360         throws JspException {
361         StringBuffer results = new StringBuffer();
362 
363         ModuleConfig config =
364             TagUtils.getInstance().getModuleConfig(pageContext);
365         ValidatorResources resources =
366             (ValidatorResources) pageContext.getAttribute(
367               ValidatorPlugIn.VALIDATOR_KEY
368                 + config.getPrefix(), PageContext.APPLICATION_SCOPE);
369 
370         if (resources == null) {
371             throw new JspException(
372                 "ValidatorResources not found in application scope under key \""
373                 + ValidatorPlugIn.VALIDATOR_KEY + config.getPrefix() + "\"");
374         }
375 
376         Locale locale =
377             TagUtils.getInstance().getUserLocale(this.pageContext, null);
378 
379         Form form = null;
380         if ("true".equalsIgnoreCase(dynamicJavascript)) {
381             form = resources.getForm(locale, formName);
382             if (form == null) {
383                 throw new JspException("No form found under '" + formName
384                     + "' in locale '" + locale
385                     + "'.  A form must be defined in the "
386                     + "Commons Validator configuration when "
387                     + "dynamicJavascript=\"true\" is set.");
388             }
389         }
390 
391         if (form != null) {
392             if ("true".equalsIgnoreCase(dynamicJavascript)) {
393                 results.append(this.createDynamicJavascript(config, resources,
394                         locale, form));
395             } else if ("true".equalsIgnoreCase(staticJavascript)) {
396                 results.append(this.renderStartElement());
397 
398                 if ("true".equalsIgnoreCase(htmlComment)) {
399                     results.append(HTML_BEGIN_COMMENT);
400                 }
401             }
402         }
403 
404         if ("true".equalsIgnoreCase(staticJavascript)) {
405             results.append(getJavascriptStaticMethods(resources));
406         }
407 
408         if ((form != null)
409             && ("true".equalsIgnoreCase(dynamicJavascript)
410             || "true".equalsIgnoreCase(staticJavascript))) {
411             results.append(getJavascriptEnd());
412         }
413 
414         return results.toString();
415     }
416 
417     /***
418      * Generates the dynamic JavaScript for the form.
419      *
420      * @param config
421      * @param resources
422      * @param locale
423      * @param form
424      */
425     private String createDynamicJavascript(ModuleConfig config,
426         ValidatorResources resources, Locale locale, Form form)
427         throws JspException {
428         StringBuffer results = new StringBuffer();
429 
430         MessageResources messages =
431             TagUtils.getInstance().retrieveMessageResources(pageContext,
432                 bundle, true);
433 
434         HttpServletRequest request =
435             (HttpServletRequest) pageContext.getRequest();
436         ServletContext application = pageContext.getServletContext();
437 
438         List actions = this.createActionList(resources, form);
439 
440         final String methods =
441             this.createMethods(actions, this.stopOnError(config));
442 
443         String formName = form.getName();
444 
445         jsFormName = formName;
446 
447         if (jsFormName.charAt(0) == '/') {
448             String mappingName =
449                 TagUtils.getInstance().getActionMappingName(jsFormName);
450             ActionMapping mapping =
451                 (ActionMapping) config.findActionConfig(mappingName);
452 
453             if (mapping == null) {
454                 JspException e =
455                     new JspException(messages.getMessage("formTag.mapping",
456                             mappingName));
457 
458                 pageContext.setAttribute(Globals.EXCEPTION_KEY, e,
459                     PageContext.REQUEST_SCOPE);
460                 throw e;
461             }
462 
463             jsFormName = mapping.getAttribute();
464         }
465 
466         results.append(this.getJavascriptBegin(methods));
467 
468         for (Iterator i = actions.iterator(); i.hasNext();) {
469             ValidatorAction va = (ValidatorAction) i.next();
470             int jscriptVar = 0;
471             String functionName = null;
472 
473             if ((va.getJsFunctionName() != null)
474                 && (va.getJsFunctionName().length() > 0)) {
475                 functionName = va.getJsFunctionName();
476             } else {
477                 functionName = va.getName();
478             }
479 
480             results.append("    function " + jsFormName + "_" + functionName
481                 + " () { \n");
482 
483             for (Iterator x = form.getFields().iterator(); x.hasNext();) {
484                 Field field = (Field) x.next();
485 
486                 // Skip indexed fields for now until there is a good way to
487                 // handle error messages (and the length of the list (could
488                 // retrieve from scope?))
489                 if (field.isIndexed() || (field.getPage() != page)
490                     || !field.isDependency(va.getName())) {
491                     continue;
492                 }
493 
494                 String message =
495                     Resources.getMessage(application, request, messages,
496                         locale, va, field);
497 
498                 message = (message != null) ? message : "";
499 
500                 // prefix variable with 'a' to make it a legal identifier
501                 results.append("     this.a" + jscriptVar++ + " = new Array(\""
502                     + field.getKey() + "\", \"" + escapeQuotes(message)
503                     + "\", ");
504 
505                 results.append("new Function (\"varName\", \"");
506 
507                 Map vars = field.getVars();
508 
509                 // Loop through the field's variables.
510                 Iterator varsIterator = vars.keySet().iterator();
511 
512                 while (varsIterator.hasNext()) {
513                     String varName = (String) varsIterator.next();
514                     Var var = (Var) vars.get(varName);
515                     String varValue =
516                         Resources.getVarValue(var, application, request, false);
517                     String jsType = var.getJsType();
518 
519                     // skip requiredif variables field, fieldIndexed, fieldTest,
520                     // fieldValue
521                     if (varName.startsWith("field")) {
522                         continue;
523                     }
524 
525                     String varValueEscaped = escapeJavascript(varValue);
526 
527                     if (Var.JSTYPE_INT.equalsIgnoreCase(jsType)) {
528                         results.append("this." + varName + "="
529                             + varValueEscaped + "; ");
530                     } else if (Var.JSTYPE_REGEXP.equalsIgnoreCase(jsType)) {
531                         results.append("this." + varName + "=/"
532                             + varValueEscaped + "/; ");
533                     } else if (Var.JSTYPE_STRING.equalsIgnoreCase(jsType)) {
534                         results.append("this." + varName + "='"
535                             + varValueEscaped + "'; ");
536 
537                         // So everyone using the latest format doesn't need to
538                         // change their xml files immediately.
539                     } else if ("mask".equalsIgnoreCase(varName)) {
540                         results.append("this." + varName + "=/"
541                             + varValueEscaped + "/; ");
542                     } else {
543                         results.append("this." + varName + "='"
544                             + varValueEscaped + "'; ");
545                     }
546                 }
547 
548                 results.append(" return this[varName];\"));\n");
549             }
550 
551             results.append("    } \n\n");
552         }
553 
554         return results.toString();
555     }
556 
557     private String escapeQuotes(String in) {
558         if ((in == null) || (in.indexOf("\"") == -1)) {
559             return in;
560         }
561 
562         StringBuffer buffer = new StringBuffer();
563         StringTokenizer tokenizer = new StringTokenizer(in, "\"", true);
564 
565         while (tokenizer.hasMoreTokens()) {
566             String token = tokenizer.nextToken();
567 
568             if (token.equals("\"")) {
569                 buffer.append("//");
570             }
571 
572             buffer.append(token);
573         }
574 
575         return buffer.toString();
576     }
577 
578     /***
579      * <p>Backslash-escapes the following characters from the input string:
580      * &quot;, &apos;, \, \r, \n.</p>
581      *
582      * <p>This method escapes characters that will result in an invalid
583      * Javascript statement within the validator Javascript.</p>
584      *
585      * @param str The string to escape.
586      * @return The string <code>s</code> with each instance of a double quote,
587      *         single quote, backslash, carriage-return, or line feed escaped
588      *         with a leading backslash.
589      */
590     private String escapeJavascript(String str) {
591         if (str == null) {
592             return null;
593         }
594 
595         int length = str.length();
596 
597         if (length == 0) {
598             return str;
599         }
600 
601         // guess at how many chars we'll be adding...
602         StringBuffer out = new StringBuffer(length + 4);
603 
604         // run through the string escaping sensitive chars
605         for (int i = 0; i < length; i++) {
606             char c = str.charAt(i);
607 
608             if ((c == '"') || (c == '\'') || (c == '//') || (c == '\n')
609                 || (c == '\r')) {
610                 out.append('//');
611             }
612 
613             out.append(c);
614         }
615 
616         return out.toString();
617     }
618 
619     /***
620      * Determines if validations should stop on an error.
621      *
622      * @param config The <code>ModuleConfig</code> used to lookup the
623      *               stopOnError setting.
624      * @return <code>true</code> if validations should stop on errors.
625      */
626     private boolean stopOnError(ModuleConfig config) {
627         Object stopOnErrorObj =
628             pageContext.getAttribute(ValidatorPlugIn.STOP_ON_ERROR_KEY + '.'
629                 + config.getPrefix(), PageContext.APPLICATION_SCOPE);
630 
631         boolean stopOnError = true;
632 
633         if (stopOnErrorObj instanceof Boolean) {
634             stopOnError = ((Boolean) stopOnErrorObj).booleanValue();
635         }
636 
637         return stopOnError;
638     }
639 
640     /***
641      * Creates the JavaScript methods list from the given actions.
642      *
643      * @param actions     A List of ValidatorAction objects.
644      * @param stopOnError If true, behaves like released version of struts 1.1
645      *                    and stops after first error. If false, evaluates all
646      *                    validations.
647      * @return JavaScript methods.
648      */
649     private String createMethods(List actions, boolean stopOnError) {
650         StringBuffer methods = new StringBuffer();
651         final String methodOperator = stopOnError ? " && " : " & ";
652 
653         Iterator iter = actions.iterator();
654 
655         while (iter.hasNext()) {
656             ValidatorAction va = (ValidatorAction) iter.next();
657 
658             if (methods.length() > 0) {
659                 methods.append(methodOperator);
660             }
661 
662             methods.append(va.getMethod()).append("(form)");
663         }
664 
665         return methods.toString();
666     }
667 
668     /***
669      * Get List of actions for the given Form.
670      *
671      * @param resources
672      * @param form
673      * @return A sorted List of ValidatorAction objects.
674      */
675     private List createActionList(ValidatorResources resources, Form form) {
676         List actionMethods = new ArrayList();
677 
678         Iterator iterator = form.getFields().iterator();
679 
680         while (iterator.hasNext()) {
681             Field field = (Field) iterator.next();
682 
683             for (Iterator x = field.getDependencyList().iterator();
684                 x.hasNext();) {
685                 Object o = x.next();
686 
687                 if ((o != null) && !actionMethods.contains(o)) {
688                     actionMethods.add(o);
689                 }
690             }
691         }
692 
693         List actions = new ArrayList();
694 
695         // Create list of ValidatorActions based on actionMethods
696         iterator = actionMethods.iterator();
697 
698         while (iterator.hasNext()) {
699             String depends = (String) iterator.next();
700             ValidatorAction va = resources.getValidatorAction(depends);
701 
702             // throw nicer NPE for easier debugging
703             if (va == null) {
704                 throw new NullPointerException("Depends string \"" + depends
705                     + "\" was not found in validator-rules.xml.");
706             }
707 
708             if ((va.getJavascript() != null)
709                 && (va.getJavascript().length() > 0)) {
710                 actions.add(va);
711             } else {
712                 iterator.remove();
713             }
714         }
715 
716         Collections.sort(actions, actionComparator);
717 
718         return actions;
719     }
720 
721     /***
722      * Release any acquired resources.
723      */
724     public void release() {
725         super.release();
726         bundle = Globals.MESSAGES_KEY;
727         formName = null;
728         jsFormName = null;
729         page = 0;
730         methodName = null;
731         staticJavascript = "true";
732         dynamicJavascript = "true";
733         htmlComment = "true";
734         cdata = "true";
735         src = null;
736     }
737 
738     /***
739      * Returns the opening script element and some initial javascript.
740      */
741     protected String getJavascriptBegin(String methods) {
742         StringBuffer sb = new StringBuffer();
743         String name = jsFormName.replace('/', '_'); // remove any '/' characters
744 
745         name =
746             jsFormName.substring(0, 1).toUpperCase()
747             + jsFormName.substring(1, jsFormName.length());
748 
749         sb.append(this.renderStartElement());
750 
751         if (this.isXhtml() && "true".equalsIgnoreCase(this.cdata)) {
752             sb.append("//<![CDATA[\r\n");
753         }
754 
755         if (!this.isXhtml() && "true".equals(htmlComment)) {
756             sb.append(HTML_BEGIN_COMMENT);
757         }
758 
759         sb.append("\n    var bCancel = false; \n\n");
760 
761         if ((methodName == null) || (methodName.length() == 0)) {
762             sb.append("    function validate" + name + "(form) { \n");
763         } else {
764             sb.append("    function " + methodName + "(form) { \n");
765         }
766 
767         sb.append("        if (bCancel) { \n");
768         sb.append("            return true; \n");
769         sb.append("        } else { \n");
770 
771         // Always return true if there aren't any Javascript validation methods
772         if ((methods == null) || (methods.length() == 0)) {
773             sb.append("            return true; \n");
774         } else {
775             sb.append("            var formValidationResult; \n");
776             sb.append("            formValidationResult = " + methods + "; \n");
777             if (methods.indexOf("&&") >= 0) {
778                 sb.append("            return (formValidationResult); \n");
779             } else {
780                 //Making Sure that Bitwise operator works:
781                 sb.append("            return (formValidationResult == 1); \n");
782             }
783         }
784         sb.append("        } \n");
785         sb.append("    } \n\n");
786 
787         return sb.toString();
788     }
789 
790     protected String getJavascriptStaticMethods(ValidatorResources resources) {
791         StringBuffer sb = new StringBuffer();
792 
793         sb.append("\n\n");
794 
795         Iterator actions = resources.getValidatorActions().values().iterator();
796 
797         while (actions.hasNext()) {
798             ValidatorAction va = (ValidatorAction) actions.next();
799 
800             if (va != null) {
801                 String javascript = va.getJavascript();
802 
803                 if ((javascript != null) && (javascript.length() > 0)) {
804                     sb.append(javascript + "\n");
805                 }
806             }
807         }
808 
809         return sb.toString();
810     }
811 
812     /***
813      * Returns the closing script element.
814      */
815     protected String getJavascriptEnd() {
816         StringBuffer sb = new StringBuffer();
817 
818         sb.append("\n");
819 
820         if (!this.isXhtml() && "true".equals(htmlComment)) {
821             sb.append(HTML_END_COMMENT);
822         }
823 
824         if (this.isXhtml() && "true".equalsIgnoreCase(this.cdata)) {
825             sb.append("//]]>\r\n");
826         }
827 
828         sb.append("</script>\n\n");
829 
830         return sb.toString();
831     }
832 
833     /***
834      * Constructs the beginning &lt;script&gt; element depending on XHTML
835      * status.
836      *
837      * @since Struts 1.2
838      */
839     protected String renderStartElement() {
840         StringBuffer start =
841             new StringBuffer("<script type=\"text/javascript\"");
842 
843         // there is no language attribute in XHTML
844         if (!this.isXhtml() && this.scriptLanguage) {
845             start.append(" language=\"Javascript1.1\"");
846         }
847 
848         if (this.src != null) {
849             start.append(" src=\"" + src + "\"");
850         }
851 
852         start.append("> \n");
853 
854         return start.toString();
855     }
856 
857     /***
858      * Returns true if this is an xhtml page.
859      */
860     private boolean isXhtml() {
861         return TagUtils.getInstance().isXhtml(this.pageContext);
862     }
863 
864     /***
865      * Returns the cdata setting "true" or "false".
866      *
867      * @return String - "true" if JavaScript will be hidden in a CDATA
868      *         section
869      */
870     public String getCdata() {
871         return cdata;
872     }
873 
874     /***
875      * Sets the cdata status.
876      *
877      * @param cdata The cdata to set
878      */
879     public void setCdata(String cdata) {
880         this.cdata = cdata;
881     }
882 
883     /***
884      * Gets whether or not the &lt;script&gt; element will include the
885      * language attribute.
886      *
887      * @return true if language attribute will be included.
888      * @since Struts 1.2
889      */
890     public boolean getScriptLanguage() {
891         return this.scriptLanguage;
892     }
893 
894     /***
895      * Sets whether or not the &lt;script&gt; element will include the
896      * language attribute.
897      *
898      * @since Struts 1.2
899      */
900     public void setScriptLanguage(boolean scriptLanguage) {
901         this.scriptLanguage = scriptLanguage;
902     }
903 }