View Javadoc

1   /*
2    * $Id: UIBean.java 651946 2008-04-27 13:41:38Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.components;
23  
24  import java.io.Writer;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.apache.struts2.StrutsConstants;
35  import org.apache.struts2.util.TextProviderHelper;
36  import org.apache.struts2.components.template.Template;
37  import org.apache.struts2.components.template.TemplateEngine;
38  import org.apache.struts2.components.template.TemplateEngineManager;
39  import org.apache.struts2.components.template.TemplateRenderingContext;
40  import org.apache.struts2.views.annotations.StrutsTagAttribute;
41  import org.apache.struts2.views.util.ContextUtil;
42  
43  import com.opensymphony.xwork2.config.ConfigurationException;
44  import com.opensymphony.xwork2.inject.Inject;
45  import com.opensymphony.xwork2.util.ValueStack;
46  import com.opensymphony.xwork2.util.logging.Logger;
47  import com.opensymphony.xwork2.util.logging.LoggerFactory;
48  
49  /***
50   * UIBean is the standard superclass of all Struts UI componentns.
51   * It defines common Struts and html properties all UI components should present for usage.
52   *
53   * <!-- START SNIPPET: templateRelatedAttributes -->
54   *
55   * <table border="1">
56   *    <thead>
57   *       <tr>
58   *          <td>Attribute</td>
59   *          <td>Theme</td>
60   *          <td>Data Types</td>
61   *          <td>Description</td>
62   *       </tr>
63   *    </thead>
64   *    <tbody>
65   *       <tr>
66   *          <td>templateDir</td>
67   *          <td>n/a</td>
68   *          <td>String</td>
69   *          <td>define the template directory</td>
70   *       </td>
71   *       <tr>
72   *          <td>theme</td>
73   *          <td>n/a</td>
74   *          <td>String</td>
75   *          <td>define the theme name</td>
76   *       </td>
77   *       <tr>
78   *          <td>template</td>
79   *          <td>n/a</td>
80   *          <td>String</td>
81   *          <td>define the template name</td>
82   *       </td>
83   *    </tbody>
84   * </table>
85   *
86   * <!-- END SNIPPET: templateRelatedAttributes -->
87   *
88   * <p/>
89   *
90   * <!-- START SNIPPET: generalAttributes -->
91   *
92   * <table border="1">
93   *    <thead>
94   *       <tr>
95   *          <td>Attribute</td>
96   *          <td>Theme</td>
97   *          <td>Data Types</td>
98   *          <td>Description</td>
99   *       </tr>
100  *    </thead>
101  *    <tbody>
102  *       <tr>
103  *          <td>cssClass</td>
104  *          <td>simple</td>
105  *          <td>String</td>
106  *          <td>define html class attribute</td>
107  *       </tr>
108  *       <tr>
109  *          <td>cssStyle</td>
110  *          <td>simple</td>
111  *          <td>String</td>
112  *          <td>define html style attribute</td>
113  *       </tr>
114  *       <tr>
115  *          <td>cssClass</td>
116  *          <td>simple</td>
117  *          <td>String</td>
118  *          <td>error class attribute</td>
119  *       </tr>
120  *       <tr>
121  *          <td>cssStyle</td>
122  *          <td>simple</td>
123  *          <td>String</td>
124  *          <td>error style attribute</td>
125  *       </tr>
126  *       <tr>
127  *          <td>title</td>
128  *          <td>simple</td>
129  *          <td>String</td>
130  *          <td>define html title attribute</td>
131  *       </tr>
132  *       <tr>
133  *          <td>disabled</td>
134  *          <td>simple</td>
135  *          <td>String</td>
136  *          <td>define html disabled attribute</td>
137  *       </tr>
138  *       <tr>
139  *          <td>label</td>
140  *          <td>xhtml</td>
141  *          <td>String</td>
142  *          <td>define label of form element</td>
143  *       </tr>
144  *       <tr>
145  *          <td>labelPosition</td>
146  *          <td>xhtml</td>
147  *          <td>String</td>
148  *          <td>define label position of form element (top/left), default to left</td>
149  *       </tr>
150  *       <tr>
151  *          <td>requiredposition</td>
152  *          <td>xhtml</td>
153  *          <td>String</td>
154  *          <td>define required label position of form element (left/right), default to right</td>
155  *       </tr>
156  *       <tr>
157  *          <td>name</td>
158  *          <td>simple</td>
159  *          <td>String</td>
160  *          <td>Form Element's field name mapping</td>
161  *       </tr>
162  *       <tr>
163  *          <td>required</td>
164  *          <td>xhtml</td>
165  *          <td>Boolean</td>
166  *          <td>add * to label (true to add false otherwise)</td>
167  *       </tr>
168  *       <tr>
169  *          <td>tabIndex</td>
170  *          <td>simple</td>
171  *          <td>String</td>
172  *          <td>define html tabindex attribute</td>
173  *       </tr>
174  *       <tr>
175  *          <td>value</td>
176  *          <td>simple</td>
177  *          <td>Object</td>
178  *          <td>define value of form element</td>
179  *       </tr>
180  *    </tbody>
181  * </table>
182  *
183  * <!-- END SNIPPET: generalAttributes -->
184  *
185  * <p/>
186  *
187  * <!-- START SNIPPET: javascriptRelatedAttributes -->
188  *
189  * <table border="1">
190  *    <thead>
191  *       <tr>
192  *          <td>Attribute</td>
193  *          <td>Theme</td>
194  *          <td>Data Types</td>
195  *          <td>Description</td>
196  *       </tr>
197  *    </thead>
198  *    <tbody>
199  *       <tr>
200  *          <td>onclick</td>
201  *          <td>simple</td>
202  *          <td>String</td>
203  *          <td>html javascript onclick attribute</td>
204  *       </tr>
205  *       <tr>
206  *          <td>ondblclick</td>
207  *          <td>simple</td>
208  *          <td>String</td>
209  *          <td>html javascript ondbclick attribute</td>
210  *       </tr>
211  *       <tr>
212  *          <td>onmousedown</td>
213  *          <td>simple</td>
214  *          <td>String</td>
215  *          <td>html javascript onmousedown attribute</td>
216  *       </tr>
217  *       <tr>
218  *          <td>onmouseup</td>
219  *          <td>simple</td>
220  *          <td>String</td>
221  *          <td>html javascript onmouseup attribute</td>
222  *       </tr>
223  *       <tr>
224  *          <td>onmouseover</td>
225  *          <td>simple</td>
226  *          <td>String</td>
227  *          <td>html javascript onmouseover attribute</td>
228  *       </tr>
229  *       <tr>
230  *          <td>onmouseout</td>
231  *          <td>simple</td>
232  *          <td>String</td>
233  *          <td>html javascript onmouseout attribute</td>
234  *       </tr>
235  *       <tr>
236  *          <td>onfocus</td>
237  *          <td>simple</td>
238  *          <td>String</td>
239  *          <td>html javascript onfocus attribute</td>
240  *       </tr>
241  *       <tr>
242  *          <td>onblur</td>
243  *          <td>simple</td>
244  *          <td>String</td>
245  *          <td>html javascript onblur attribute</td>
246  *       </tr>
247  *       <tr>
248  *          <td>onkeypress</td>
249  *          <td>simple</td>
250  *          <td>String</td>
251  *          <td>html javascript onkeypress attribute</td>
252  *       </tr>
253  *       <tr>
254  *          <td>onkeyup</td>
255  *          <td>simple</td>
256  *          <td>String</td>
257  *          <td>html javascript onkeyup attribute</td>
258  *       </tr>
259  *       <tr>
260  *          <td>onkeydown</td>
261  *          <td>simple</td>
262  *          <td>String</td>
263  *          <td>html javascript onkeydown attribute</td>
264  *       </tr>
265  *       <tr>
266  *          <td>onselect</td>
267  *          <td>simple</td>
268  *          <td>String</td>
269  *          <td>html javascript onselect attribute</td>
270  *       </tr>
271  *       <tr>
272  *          <td>onchange</td>
273  *          <td>simple</td>
274  *          <td>String</td>
275  *          <td>html javascript onchange attribute</td>
276  *       </tr>
277  *    </tbody>
278  * </table>
279  *
280  * <!-- END SNIPPET: javascriptRelatedAttributes -->
281  *
282  * <p/>
283  *
284  * <!-- START SNIPPET: tooltipattributes -->
285  *
286  * <table border="1">
287  *  <tr>
288  *     <td>Attribute</td>
289  *     <td>Data Type</td>
290  *     <td>Default</td>
291  *     <td>Description</td>
292  *  </tr>
293  *  <tr>
294  *      <td>tooltip</td>
295  *      <td>String</td>
296  *      <td>none</td>
297  *      <td>Set the tooltip of this particular component</td>
298  *  </tr>
299  *  <tr>
300  *      <td>jsTooltipEnabled</td>
301  *      <td>String</td>
302  *      <td>false</td>
303  *      <td>Enable js tooltip rendering</td>
304  *  </tr>
305  *    <tr>
306  *      <td>tooltipIcon</td>
307  *      <td>String</td>
308  *      <td>/struts/static/tooltip/tooltip.gif</td>
309  *      <td>The url to the tooltip icon</td>
310  *   <tr>
311  *      <td>tooltipDelay</td>
312  *      <td>String</td>
313  *      <td>500</td>
314  *      <td>Tooltip shows up after the specified timeout (miliseconds). A behavior similar to that of OS based tooltips.</td>
315  *   </tr>
316  *   <tr>
317  *      <td>key</td>
318  *      <td>simple</td>
319  *      <td>String</td>
320  *      <td>The name of the property this input field represents.  This will auto populate the name, label, and value</td>
321  *   </tr>
322  * </table>
323  *
324  * <!-- END SNIPPET: tooltipattributes -->
325  *
326  *
327  * <!-- START SNIPPET: tooltipdescription -->
328  * <b>tooltipConfig is deprecated, use individual tooltip configuration attributes instead </b>
329  *
330  * Every Form UI component (in xhtml / css_xhtml or any other that extends them) can
331  * have tooltips assigned to them. The Form component's tooltip related attribute, once
332  * defined, will be applied to all form UI components that are created under it unless
333  * explicitly overriden by having the Form UI component itself defined with their own tooltip attribute.
334  *
335  * <p/>
336  *
337  * In Example 1, the textfield will inherit the tooltipDelay and tooltipIconPath attribte from
338  * its containing form. In other words, although it doesn't define a tooltipIconPath
339  * attribute, it will have that attribute inherited from its containing form.
340  *
341  * <p/>
342  *
343  * In Example 2, the  textfield will inherite both the tooltipDelay and
344  * tooltipIconPath attribute from its containing form, but the tooltipDelay
345  * attribute is overriden at the textfield itself. Hence, the textfield actually will
346  * have its tooltipIcon defined as /myImages/myIcon.gif, inherited from its containing form, and
347  * tooltipDelay defined as 5000.
348  *
349  * <p/>
350  *
351  * Example 3, 4 and 5 show different ways of setting the tooltip configuration attribute.<br/>
352  * <b>Example 3:</b> Set tooltip config through the body of the param tag<br/>
353  * <b>Example 4:</b> Set tooltip config through the value attribute of the param tag<br/>
354  * <b>Example 5:</b> Set tooltip config through the tooltip attributes of the component tag<br/>
355  *
356  * <!-- END SNIPPET: tooltipdescription -->
357  *
358  *
359  * <pre>
360  * <!-- START SNIPPET: tooltipexample -->
361  *
362  * &lt;!-- Example 1: --&gt;
363  * &lt;s:form
364  *          tooltipDelay="500"
365  *          tooltipIconPath="/myImages/myIcon.gif" .... &gt;
366  *   ....
367  *     &lt;s:textfield label="Customer Name" tooltip="Enter the customer name" .... /&gt;
368  *   ....
369  * &lt;/s:form&gt;
370  *
371  * &lt;!-- Example 2: --&gt;
372  * &lt;s:form
373  *          tooltipDelay="500"
374  *          tooltipIconPath="/myImages/myIcon.gif" .... &gt;
375  *   ....
376  *     &lt;s:textfield label="Address"
377  *          tooltip="Enter your address"
378  *          tooltipDelay="5000" /&gt;
379  *   ....
380  * &lt;/s:form&gt;
381  *
382  *
383  * &lt;-- Example 3: --&gt;
384  * &lt;s:textfield
385  *        label="Customer Name"
386  *        tooltip="One of our customer Details"&gt;
387  *        &lt;s:param name="tooltipDelay"&gt;
388  *             500
389  *        &lt;/s:param&gt;
390  *        &lt;s:param name="tooltipIconPath"&gt;
391  *             /myImages/myIcon.gif
392  *        &lt;/s:param&gt;
393  * &lt;/s:textfield&gt;
394  *
395  *
396  * &lt;-- Example 4: --&gt;
397  * &lt;s:textfield
398  *          label="Customer Address"
399  *          tooltip="Enter The Customer Address" &gt;
400  *          &lt;s:param
401  *              name="tooltipDelay"
402  *              value="500" /&gt;
403  * &lt;/s:textfield&gt;
404  *
405  *
406  * &lt;-- Example 5: --&gt;
407  * &lt;s:textfield
408  *          label="Customer Telephone Number"
409  *          tooltip="Enter customer Telephone Number"
410  *          tooltipDelay="500"
411  *          tooltipIconPath="/myImages/myIcon.gif" /&gt;
412  *
413  * <!-- END SNIPPET: tooltipexample -->
414  * </pre>
415  *
416  */
417 public abstract class UIBean extends Component {
418     private static final Logger LOG = LoggerFactory.getLogger(UIBean.class);
419 
420     protected HttpServletRequest request;
421     protected HttpServletResponse response;
422 
423     public UIBean(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
424         super(stack);
425         this.request = request;
426         this.response = response;
427         this.templateSuffix = ContextUtil.getTemplateSuffix(stack.getContext());
428     }
429 
430     // The templateSuffic to use, overrides the default one if not null.
431     protected String templateSuffix;
432 
433     // The template to use, overrides the default one.
434     protected String template;
435 
436     // templateDir and theme attributes
437     protected String templateDir;
438     protected String theme;
439 
440     // shortcut, sets label, name, and value
441     protected String key;
442 
443     protected String id;
444     protected String cssClass;
445     protected String cssStyle;
446     protected String cssErrorClass;
447     protected String cssErrorStyle;
448     protected String disabled;
449     protected String label;
450     protected String labelPosition;
451     protected String labelSeparator;
452     protected String requiredposition;
453     protected String name;
454     protected String required;
455     protected String tabindex;
456     protected String value;
457     protected String title;
458 
459     // HTML scripting events attributes
460     protected String onclick;
461     protected String ondblclick;
462     protected String onmousedown;
463     protected String onmouseup;
464     protected String onmouseover;
465     protected String onmousemove;
466     protected String onmouseout;
467     protected String onfocus;
468     protected String onblur;
469     protected String onkeypress;
470     protected String onkeydown;
471     protected String onkeyup;
472     protected String onselect;
473     protected String onchange;
474 
475     // common html attributes
476     protected String accesskey;
477 
478     // javascript tooltip attribute
479     protected String tooltip;
480     protected String tooltipConfig;
481     protected String javascriptTooltip;
482     protected String tooltipDelay;
483     protected String tooltipCssClass;
484     protected String tooltipIconPath;
485 
486     // dynamic attributes
487     protected Map<String,Object> dynamicAttributes = new HashMap<String,Object>();
488 
489     protected String defaultTemplateDir;
490     protected String defaultUITheme;
491     protected TemplateEngineManager templateEngineManager;
492 
493     @Inject(StrutsConstants.STRUTS_UI_TEMPLATEDIR)
494     public void setDefaultTemplateDir(String dir) {
495         this.defaultTemplateDir = dir;
496     }
497 
498     @Inject(StrutsConstants.STRUTS_UI_THEME)
499     public void setDefaultUITheme(String theme) {
500         this.defaultUITheme = theme;
501     }
502 
503     @Inject
504     public void setTemplateEngineManager(TemplateEngineManager mgr) {
505         this.templateEngineManager = mgr;
506     }
507 
508     public boolean end(Writer writer, String body) {
509         evaluateParams();
510         try {
511             super.end(writer, body, false);
512             mergeTemplate(writer, buildTemplateName(template, getDefaultTemplate()));
513         } catch (Exception e) {
514             LOG.error("error when rendering", e);
515         }
516         finally {
517             popComponentStack();
518         }
519 
520         return false;
521     }
522 
523     /***
524      * A contract that requires each concrete UI Tag to specify which template should be used as a default.  For
525      * example, the CheckboxTab might return "checkbox.vm" while the RadioTag might return "radio.vm".  This value
526      * <strong>not</strong> begin with a '/' unless you intend to make the path absolute rather than relative to the
527      * current theme.
528      *
529      * @return The name of the template to be used as the default.
530      */
531     protected abstract String getDefaultTemplate();
532 
533     protected Template buildTemplateName(String myTemplate, String myDefaultTemplate) {
534         String template = myDefaultTemplate;
535 
536         if (myTemplate != null) {
537             template = findString(myTemplate);
538         }
539 
540         String templateDir = getTemplateDir();
541         String theme = getTheme();
542 
543         return new Template(templateDir, theme, template);
544 
545     }
546 
547     protected void mergeTemplate(Writer writer, Template template) throws Exception {
548         final TemplateEngine engine = templateEngineManager.getTemplateEngine(template, templateSuffix);
549         if (engine == null) {
550             throw new ConfigurationException("Unable to find a TemplateEngine for template " + template);
551         }
552 
553         if (LOG.isDebugEnabled()) {
554             LOG.debug("Rendering template " + template);
555         }
556 
557         final TemplateRenderingContext context = new TemplateRenderingContext(template, writer, getStack(), getParameters(), this);
558         engine.renderTemplate(context);
559     }
560 
561     public String getTemplateDir() {
562         String templateDir = null;
563 
564         if (this.templateDir != null) {
565             templateDir = findString(this.templateDir);
566         }
567 
568         // If templateDir is not explicitly given,
569         // try to find attribute which states the dir set to use
570         if ((templateDir == null) || (templateDir.equals(""))) {
571             templateDir = stack.findString("#attr.templateDir");
572         }
573 
574         // Default template set
575         if ((templateDir == null) || (templateDir.equals(""))) {
576             templateDir = defaultTemplateDir;
577         }
578 
579         // Defaults to 'template'
580         if ((templateDir == null) || (templateDir.equals(""))) {
581             templateDir = "template";
582         }
583 
584         return templateDir;
585     }
586 
587     public String getTheme() {
588         String theme = null;
589 
590         if (this.theme != null) {
591             theme = findString(this.theme);
592         }
593 
594         if ( theme == null || theme.equals("") ) {
595             Form form = (Form) findAncestor(Form.class);
596             if (form != null) {
597                 theme = form.getTheme();
598             }
599         }
600 
601         // If theme set is not explicitly given,
602         // try to find attribute which states the theme set to use
603         if ((theme == null) || (theme.equals(""))) {
604             theme = stack.findString("#attr.theme");
605         }
606 
607         // Default theme set
608         if ((theme == null) || (theme.equals(""))) {
609             theme = defaultUITheme;
610         }
611 
612         return theme;
613     }
614 
615     public void evaluateParams() {
616         addParameter("templateDir", getTemplateDir());
617         addParameter("theme", getTheme());
618         addParameter("dynamicAttributes", dynamicAttributes);
619 
620         String name = null;
621         String providedLabel = null;
622 
623         if (this.key != null) {
624 
625             if(this.name == null) {
626                 this.name = key;
627             }
628 
629             if(this.label == null) {
630                 // lookup the label from a TextProvider (default value is the key)
631                 providedLabel = TextProviderHelper.getText(key, key, stack);
632             }
633 
634         }
635 
636         if (this.name != null) {
637             name = findString(this.name);
638             addParameter("name", name);
639         }
640 
641         if (label != null) {
642             addParameter("label", findString(label));
643         } else {
644             if (providedLabel != null) {
645                 // label found via a TextProvider
646                 addParameter("label", providedLabel);
647             }
648         }
649 
650         if (labelSeparator != null) {
651             addParameter("labelseparator", findString(labelSeparator));
652         }
653 
654         if (labelPosition != null) {
655             addParameter("labelposition", findString(labelPosition));
656         }
657 
658         if (requiredposition != null) {
659             addParameter("requiredposition", findString(requiredposition));
660         }
661 
662         if (required != null) {
663             addParameter("required", findValue(required, Boolean.class));
664         }
665 
666         if (disabled != null) {
667             addParameter("disabled", findValue(disabled, Boolean.class));
668         }
669 
670         if (tabindex != null) {
671             addParameter("tabindex", findString(tabindex));
672         }
673 
674         if (onclick != null) {
675             addParameter("onclick", findString(onclick));
676         }
677 
678         if (ondblclick != null) {
679             addParameter("ondblclick", findString(ondblclick));
680         }
681 
682         if (onmousedown != null) {
683             addParameter("onmousedown", findString(onmousedown));
684         }
685 
686         if (onmouseup != null) {
687             addParameter("onmouseup", findString(onmouseup));
688         }
689 
690         if (onmouseover != null) {
691             addParameter("onmouseover", findString(onmouseover));
692         }
693 
694         if (onmousemove != null) {
695             addParameter("onmousemove", findString(onmousemove));
696         }
697 
698         if (onmouseout != null) {
699             addParameter("onmouseout", findString(onmouseout));
700         }
701 
702         if (onfocus != null) {
703             addParameter("onfocus", findString(onfocus));
704         }
705 
706         if (onblur != null) {
707             addParameter("onblur", findString(onblur));
708         }
709 
710         if (onkeypress != null) {
711             addParameter("onkeypress", findString(onkeypress));
712         }
713 
714         if (onkeydown != null) {
715             addParameter("onkeydown", findString(onkeydown));
716         }
717 
718         if (onkeyup != null) {
719             addParameter("onkeyup", findString(onkeyup));
720         }
721 
722         if (onselect != null) {
723             addParameter("onselect", findString(onselect));
724         }
725 
726         if (onchange != null) {
727             addParameter("onchange", findString(onchange));
728         }
729 
730         if (accesskey != null) {
731             addParameter("accesskey", findString(accesskey));
732         }
733 
734         if (cssClass != null) {
735             addParameter("cssClass", findString(cssClass));
736         }
737 
738         if (cssStyle != null) {
739             addParameter("cssStyle", findString(cssStyle));
740         }
741 
742         if (cssErrorClass != null) {
743             addParameter("cssErrorClass", findString(cssErrorClass));
744         }
745 
746         if (cssErrorStyle != null) {
747             addParameter("cssErrorStyle", findString(cssErrorStyle));
748         }
749 
750         if (title != null) {
751             addParameter("title", findString(title));
752         }
753 
754 
755         // see if the value was specified as a parameter already
756         if (parameters.containsKey("value")) {
757             parameters.put("nameValue", parameters.get("value"));
758         } else {
759             if (evaluateNameValue()) {
760                 final Class valueClazz = getValueClassType();
761 
762                 if (valueClazz != null) {
763                     if (value != null) {
764                         addParameter("nameValue", findValue(value, valueClazz));
765                     } else if (name != null) {
766                         String expr = name;
767                         if (altSyntax()) {
768                             expr = "%{" + expr + "}";
769                         }
770 
771                         addParameter("nameValue", findValue(expr, valueClazz));
772                     }
773                 } else {
774                     if (value != null) {
775                         addParameter("nameValue", findValue(value));
776                     } else if (name != null) {
777                         addParameter("nameValue", findValue(name));
778                     }
779                 }
780             }
781         }
782 
783         final Form form = (Form) findAncestor(Form.class);
784 
785         // create HTML id element
786         populateComponentHtmlId(form);
787 
788         if (form != null ) {
789             addParameter("form", form.getParameters());
790 
791             if ( name != null ) {
792                 // list should have been created by the form component
793                 List tags = (List) form.getParameters().get("tagNames");
794                 tags.add(name);
795             }
796         }
797 
798 
799         // tooltip & tooltipConfig
800         if (tooltipConfig != null) {
801             addParameter("tooltipConfig", findValue(tooltipConfig));
802         }
803         if (tooltip != null) {
804             addParameter("tooltip", findString(tooltip));
805 
806             Map tooltipConfigMap = getTooltipConfig(this);
807 
808             if (form != null) { // inform the containing form that we need tooltip javascript included
809                 form.addParameter("hasTooltip", Boolean.TRUE);
810 
811                 // tooltipConfig defined in component itseilf will take precedence
812                 // over those defined in the containing form
813                 Map overallTooltipConfigMap = getTooltipConfig(form);
814                 overallTooltipConfigMap.putAll(tooltipConfigMap); // override parent form's tooltip config
815 
816                 for (Iterator i = overallTooltipConfigMap.entrySet().iterator(); i.hasNext(); ) {
817                     Map.Entry entry = (Map.Entry) i.next();
818                     addParameter((String) entry.getKey(), entry.getValue());
819                 }
820             }
821             else {
822                 LOG.warn("No ancestor Form found, javascript based tooltip will not work, however standard HTML tooltip using alt and title attribute will still work ");
823             }
824 
825             //TODO: this is to keep backward compatibility, remove once when tooltipConfig is dropped
826             String  jsTooltipEnabled = (String) getParameters().get("jsTooltipEnabled");
827             if (jsTooltipEnabled != null)
828                 this.javascriptTooltip = jsTooltipEnabled;
829 
830             //TODO: this is to keep backward compatibility, remove once when tooltipConfig is dropped
831             String tooltipIcon = (String) getParameters().get("tooltipIcon");
832             if (tooltipIcon != null)
833                 this.addParameter("tooltipIconPath", tooltipIcon);
834             if (this.tooltipIconPath != null)
835                 this.addParameter("tooltipIconPath", findString(this.tooltipIconPath));
836 
837             //TODO: this is to keep backward compatibility, remove once when tooltipConfig is dropped
838             String tooltipDelayParam = (String) getParameters().get("tooltipDelay");
839             if (tooltipDelayParam != null)
840                 this.addParameter("tooltipDelay", tooltipDelayParam);
841             if (this.tooltipDelay != null)
842                 this.addParameter("tooltipDelay", findString(this.tooltipDelay));
843 
844             if (this.javascriptTooltip != null) {
845                 Boolean jsTooltips = (Boolean) findValue(this.javascriptTooltip, Boolean.class);
846                 //TODO use a Boolean model when tooltipConfig is dropped
847                 this.addParameter("jsTooltipEnabled", jsTooltips.toString());
848 
849                 if (form != null)
850                     form.addParameter("hasTooltip", jsTooltips);
851                 if (this.tooltipCssClass != null)
852                     this.addParameter("tooltipCssClass", findString(this.tooltipCssClass));
853             }
854 
855 
856         }
857 
858         evaluateExtraParams();
859     }
860 
861     protected String escape(String name) {
862         // escape any possible values that can make the ID painful to work with in JavaScript
863         if (name != null) {
864             return name.replaceAll("[^a-zA-Z0-9_]", "_");
865         } else {
866             return "";
867         }
868     }
869 
870     /***
871      * Ensures an unescaped attribute value cannot be vulnerable to XSS attacks
872      *
873      * @param val The value to check
874      * @return The escaped value
875      */
876     protected String ensureAttributeSafelyNotEscaped(String val) {
877         if (val != null) {
878             return val.replaceAll("\"", "&#34;");
879         } else {
880             return "";
881         }
882     }
883 
884     protected void evaluateExtraParams() {
885     }
886 
887     protected boolean evaluateNameValue() {
888         return true;
889     }
890 
891     protected Class getValueClassType() {
892         return String.class;
893     }
894 
895     public void addFormParameter(String key, Object value) {
896         Form form = (Form) findAncestor(Form.class);
897         if (form != null) {
898             form.addParameter(key, value);
899         }
900     }
901 
902     protected void enableAncestorFormCustomOnsubmit() {
903         Form form = (Form) findAncestor(Form.class);
904         if (form != null) {
905             form.addParameter("customOnsubmitEnabled", Boolean.TRUE);
906         } else {
907             LOG.warn("Cannot find an Ancestor form, custom onsubmit is NOT enabled");
908         }
909     }
910 
911     protected Map getTooltipConfig(UIBean component) {
912         Object tooltipConfigObj = component.getParameters().get("tooltipConfig");
913         Map tooltipConfig = new LinkedHashMap();
914 
915         if (tooltipConfigObj instanceof Map) {
916             // we get this if its configured using
917             // 1] UI component's tooltipConfig attribute  OR
918             // 2] <param name="tooltip" value="" /> param tag value attribute
919 
920             tooltipConfig = new LinkedHashMap((Map)tooltipConfigObj);
921         } else if (tooltipConfigObj instanceof String) {
922 
923             // we get this if its configured using
924             // <param name="tooltipConfig"> ... </param> tag's body
925             String tooltipConfigStr = (String) tooltipConfigObj;
926             String[] tooltipConfigArray = tooltipConfigStr.split("//|");
927 
928             for (int a=0; a<tooltipConfigArray.length; a++) {
929                 String[] configEntry = ((String)tooltipConfigArray[a].trim()).split("=");
930                 String key = configEntry[0].trim();
931                 String value = null;
932                 if (configEntry.length > 1) {
933                     value = configEntry[1].trim();
934                     tooltipConfig.put(key, value);
935                 }
936                 else {
937                     LOG.warn("component "+component+" tooltip config param "+key+" has no value defined, skipped");
938                 }
939             }
940         }
941         if (component.javascriptTooltip != null)
942             tooltipConfig.put("jsTooltipEnabled", component.javascriptTooltip);
943         if (component.tooltipIconPath != null)
944             tooltipConfig.put("tooltipIcon", component.tooltipIconPath);
945         if (component.tooltipDelay != null)
946             tooltipConfig.put("tooltipDelay", component.tooltipDelay);
947         return tooltipConfig;
948     }
949 
950     /***
951      * Create HTML id element for the component and populate this component parmaeter
952      * map. Additionally, a parameter named escapedId is populated which contains the found id value filtered by
953      * {@link #escape(String)}, needed eg. for naming Javascript identifiers based on the id value.
954      *
955      * The order is as follows :-
956      * <ol>
957      *   <li>This component id attribute</li>
958      *   <li>[containing_form_id]_[this_component_name]</li>
959      *   <li>[this_component_name]</li>
960      * </ol>
961      *
962      * @param form
963      */
964     protected void populateComponentHtmlId(Form form) {
965         String tryId;
966         if (id != null) {
967             // this check is needed for backwards compatibility with 2.1.x
968             if (altSyntax()) {
969                 tryId = findString(id);
970             } else {
971                 tryId = id;
972             }
973         } else if (form != null) {
974             tryId = form.getParameters().get("id") + "_"
975                     + escape(name != null ? findString(name) : null);
976         } else {
977             tryId = escape(name != null ? findString(name) : null);
978         }
979         addParameter("id", tryId);
980         addParameter("escapedId", escape(tryId));
981     }
982 
983     /***
984      * Get's the id for referencing element.
985      * @return the id for referencing element.
986      */
987     public String getId() {
988         return id;
989     }
990 
991     @StrutsTagAttribute(description="HTML id attribute")
992     public void setId(String id) {
993         if (id != null) {
994             this.id = findString(id);
995         }
996     }
997 
998     @StrutsTagAttribute(description="The template directory.")
999     public void setTemplateDir(String templateDir) {
1000         this.templateDir = templateDir;
1001     }
1002 
1003     @StrutsTagAttribute(description="The theme (other than default) to use for rendering the element")
1004     public void setTheme(String theme) {
1005         this.theme = theme;
1006     }
1007 
1008     public String getTemplate() {
1009         return template;
1010     }
1011 
1012     @StrutsTagAttribute(description="The template (other than default) to use for rendering the element")
1013     public void setTemplate(String template) {
1014         this.template = template;
1015     }
1016 
1017     @StrutsTagAttribute(description="The css class to use for element")
1018     public void setCssClass(String cssClass) {
1019         this.cssClass = cssClass;
1020     }
1021 
1022     @StrutsTagAttribute(description="The css style definitions for element to use")
1023     public void setCssStyle(String cssStyle) {
1024         this.cssStyle = cssStyle;
1025     }
1026 
1027     @StrutsTagAttribute(description="The css error class to use for element")
1028     public void setCssErrorClass(String cssErrorClass) {
1029         this.cssErrorClass = cssErrorClass;
1030     }
1031 
1032     @StrutsTagAttribute(description="The css error style definitions for element to use")
1033     public void setCssErrorStyle(String cssErrorStyle) {
1034         this.cssErrorStyle = cssErrorStyle;
1035     }
1036 
1037     @StrutsTagAttribute(description="Set the html title attribute on rendered html element")
1038     public void setTitle(String title) {
1039         this.title = title;
1040     }
1041 
1042     @StrutsTagAttribute(description="Set the html disabled attribute on rendered html element")
1043     public void setDisabled(String disabled) {
1044         this.disabled = disabled;
1045     }
1046 
1047     @StrutsTagAttribute(description="Label expression used for rendering a element specific label")
1048     public void setLabel(String label) {
1049         this.label = label;
1050     }
1051 
1052     @StrutsTagAttribute(description="String that will be appended to the labe", defaultValue=":")
1053     public void setLabelSeparator(String labelseparator) {
1054         this.labelSeparator = labelseparator;
1055     }
1056 
1057     @StrutsTagAttribute(description="Define label position of form element (top/left)")
1058     public void setLabelposition(String labelPosition) {
1059         this.labelPosition = labelPosition;
1060     }
1061 
1062     @StrutsTagAttribute(description="Define required position of required form element (left|right)")
1063     public void setRequiredposition(String requiredposition) {
1064         this.requiredposition = requiredposition;
1065     }
1066 
1067     @StrutsTagAttribute(description="The name to set for element")
1068     public void setName(String name) {
1069         this.name = name;
1070     }
1071 
1072     @StrutsTagAttribute(description="If set to true, the rendered element will indicate that input is required", type="Boolean", defaultValue="false")
1073     public void setRequired(String required) {
1074         this.required = required;
1075     }
1076 
1077     @StrutsTagAttribute(description="Set the html tabindex attribute on rendered html element")
1078     public void setTabindex(String tabindex) {
1079         this.tabindex = tabindex;
1080     }
1081 
1082     @StrutsTagAttribute(description="Preset the value of input element.")
1083     public void setValue(String value) {
1084         this.value = value;
1085     }
1086 
1087     @StrutsTagAttribute(description="Set the html onclick attribute on rendered html element")
1088     public void setOnclick(String onclick) {
1089         this.onclick = onclick;
1090     }
1091 
1092     @StrutsTagAttribute(description="Set the html ondblclick attribute on rendered html element")
1093     public void setOndblclick(String ondblclick) {
1094         this.ondblclick = ondblclick;
1095     }
1096 
1097     @StrutsTagAttribute(description="Set the html onmousedown attribute on rendered html element")
1098     public void setOnmousedown(String onmousedown) {
1099         this.onmousedown = onmousedown;
1100     }
1101 
1102     @StrutsTagAttribute(description="Set the html onmouseup attribute on rendered html element")
1103     public void setOnmouseup(String onmouseup) {
1104         this.onmouseup = onmouseup;
1105     }
1106 
1107     @StrutsTagAttribute(description="Set the html onmouseover attribute on rendered html element")
1108     public void setOnmouseover(String onmouseover) {
1109         this.onmouseover = onmouseover;
1110     }
1111 
1112     @StrutsTagAttribute(description="Set the html onmousemove attribute on rendered html element")
1113     public void setOnmousemove(String onmousemove) {
1114         this.onmousemove = onmousemove;
1115     }
1116 
1117     @StrutsTagAttribute(description="Set the html onmouseout attribute on rendered html element")
1118     public void setOnmouseout(String onmouseout) {
1119         this.onmouseout = onmouseout;
1120     }
1121 
1122     @StrutsTagAttribute(description="Set the html onfocus attribute on rendered html element")
1123     public void setOnfocus(String onfocus) {
1124         this.onfocus = onfocus;
1125     }
1126 
1127     @StrutsTagAttribute(description=" Set the html onblur attribute on rendered html element")
1128     public void setOnblur(String onblur) {
1129         this.onblur = onblur;
1130     }
1131 
1132     @StrutsTagAttribute(description="Set the html onkeypress attribute on rendered html element")
1133     public void setOnkeypress(String onkeypress) {
1134         this.onkeypress = onkeypress;
1135     }
1136 
1137     @StrutsTagAttribute(description="Set the html onkeydown attribute on rendered html element")
1138     public void setOnkeydown(String onkeydown) {
1139         this.onkeydown = onkeydown;
1140     }
1141 
1142     @StrutsTagAttribute(description="Set the html onkeyup attribute on rendered html element")
1143     public void setOnkeyup(String onkeyup) {
1144         this.onkeyup = onkeyup;
1145     }
1146 
1147     @StrutsTagAttribute(description="Set the html onselect attribute on rendered html element")
1148     public void setOnselect(String onselect) {
1149         this.onselect = onselect;
1150     }
1151 
1152     @StrutsTagAttribute(description="Set the html onchange attribute on rendered html element")
1153     public void setOnchange(String onchange) {
1154         this.onchange = onchange;
1155     }
1156 
1157     @StrutsTagAttribute(description="Set the html accesskey attribute on rendered html element")
1158     public void setAccesskey(String accesskey) {
1159         this.accesskey = accesskey;
1160     }
1161 
1162     @StrutsTagAttribute(description="Set the tooltip of this particular component")
1163     public void setTooltip(String tooltip) {
1164         this.tooltip = tooltip;
1165     }
1166 
1167     @StrutsTagAttribute(description="Deprecated. Use individual tooltip configuration attributes instead.")
1168     public void setTooltipConfig(String tooltipConfig) {
1169         this.tooltipConfig = tooltipConfig;
1170     }
1171 
1172     @StrutsTagAttribute(description="Set the key (name, value, label) for this particular component")
1173     public void setKey(String key) {
1174         this.key = key;
1175     }
1176 
1177     @StrutsTagAttribute(description="Use JavaScript to generate tooltips", type="Boolean", defaultValue="false")
1178     public void setJavascriptTooltip(String javascriptTooltip) {
1179         this.javascriptTooltip = javascriptTooltip;
1180     }
1181 
1182     @StrutsTagAttribute(description="CSS class applied to JavaScrip tooltips", defaultValue="StrutsTTClassic")
1183     public void setTooltipCssClass(String tooltipCssClass) {
1184         this.tooltipCssClass = tooltipCssClass;
1185     }
1186 
1187     @StrutsTagAttribute(description="Delay in milliseconds, before showing JavaScript tooltips ",
1188         defaultValue="Classic")
1189     public void setTooltipDelay(String tooltipDelay) {
1190         this.tooltipDelay = tooltipDelay;
1191     }
1192 
1193     @StrutsTagAttribute(description="Icon path used for image that will have the tooltip")
1194     public void setTooltipIconPath(String tooltipIconPath) {
1195         this.tooltipIconPath = tooltipIconPath;
1196     }
1197 
1198     public void setDynamicAttributes(Map<String,Object> dynamicAttributes) {
1199         this.dynamicAttributes = dynamicAttributes;
1200     }
1201 }