View Javadoc

1   /*
2    * $Id: BaseHandlerTag.java 377073 2006-02-11 22:32:59Z niallp $
3    *
4    * Copyright 1999-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.beanutils.BeanUtils;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.struts.Globals;
24  import org.apache.struts.action.ActionMessages;
25  import org.apache.struts.taglib.TagUtils;
26  import org.apache.struts.taglib.logic.IterateTag;
27  import org.apache.struts.util.MessageResources;
28  import org.apache.struts.util.RequestUtils;
29  
30  import javax.servlet.jsp.JspException;
31  import javax.servlet.jsp.PageContext;
32  import javax.servlet.jsp.tagext.BodyTagSupport;
33  
34  import java.lang.reflect.InvocationTargetException;
35  import java.lang.reflect.Method;
36  
37  /***
38   * Base class for tags that render form elements capable of including
39   * JavaScript event handlers and/or CSS Style attributes. This class does not
40   * implement the doStartTag() or doEndTag() methods. Subclasses should provide
41   * appropriate implementations of these.
42   *
43   * @version $Rev: 377073 $ $Date: 2006-02-11 15:32:59 -0700 (Sat, 11 Feb 2006) $
44   */
45  public abstract class BaseHandlerTag extends BodyTagSupport {
46      /***
47       * Commons Logging instance.
48       */
49      private static Log log = LogFactory.getLog(BaseHandlerTag.class);
50  
51      // ----------------------------------------------------- Instance Variables
52  
53      /***
54       * The message resources for this package.
55       */
56      protected static MessageResources messages =
57          MessageResources.getMessageResources(Constants.Package
58              + ".LocalStrings");
59  
60      //  Navigation Management
61  
62      /***
63       * Access key character.
64       */
65      protected String accesskey = null;
66  
67      /***
68       * Tab index value.
69       */
70      protected String tabindex = null;
71  
72      //  Indexing ability for Iterate
73  
74      /***
75       * Whether to created indexed names for fields
76       *
77       * @since Struts 1.1
78       */
79      protected boolean indexed = false;
80  
81      //  Mouse Events
82  
83      /***
84       * Mouse click event.
85       */
86      private String onclick = null;
87  
88      /***
89       * Mouse double click event.
90       */
91      private String ondblclick = null;
92  
93      /***
94       * Mouse over component event.
95       */
96      private String onmouseover = null;
97  
98      /***
99       * Mouse exit component event.
100      */
101     private String onmouseout = null;
102 
103     /***
104      * Mouse moved over component event.
105      */
106     private String onmousemove = null;
107 
108     /***
109      * Mouse pressed on component event.
110      */
111     private String onmousedown = null;
112 
113     /***
114      * Mouse released on component event.
115      */
116     private String onmouseup = null;
117 
118     //  Keyboard Events
119 
120     /***
121      * Key down in component event.
122      */
123     private String onkeydown = null;
124 
125     /***
126      * Key released in component event.
127      */
128     private String onkeyup = null;
129 
130     /***
131      * Key down and up together in component event.
132      */
133     private String onkeypress = null;
134 
135     // Text Events
136 
137     /***
138      * Text selected in component event.
139      */
140     private String onselect = null;
141 
142     /***
143      * Content changed after component lost focus event.
144      */
145     private String onchange = null;
146 
147     // Focus Events and States
148 
149     /***
150      * Component lost focus event.
151      */
152     private String onblur = null;
153 
154     /***
155      * Component has received focus event.
156      */
157     private String onfocus = null;
158 
159     /***
160      * Component is disabled.
161      */
162     private boolean disabled = false;
163 
164     /***
165      * Indicates whether 'disabled' is a valid attribute
166      */
167     protected boolean doDisabled = true;
168 
169     /***
170      * Component is readonly.
171      */
172     private boolean readonly = false;
173 
174     /***
175      * <p>Indicates whether 'readonly' is a valid attribute.</p>
176      *
177      * <p>According to the HTML 4.0 Specification &lt;readonly&gt; is valid
178      * for &lt;input type="text"&gt;, &lt;input type="password"&gt; and
179      * &lt;textarea"&gt; elements. Therefore, except for those tags this value
180      * is set to <code>false</code>.</p>
181      */
182     protected boolean doReadonly = false;
183 
184     // CSS Style Support
185 
186     /***
187      * Style attribute associated with component.
188      */
189     private String style = null;
190 
191     /***
192      * Named Style class associated with component.
193      */
194     private String styleClass = null;
195 
196     /***
197      * Identifier associated with component.
198      */
199     private String styleId = null;
200 
201     /***
202      * The request attribute key for our error messages (if any).
203      */
204     private String errorKey = Globals.ERROR_KEY;
205 
206     /***
207      * Style attribute associated with component when errors exist.
208      */
209     private String errorStyle = null;
210 
211     /***
212      * Named Style class associated with component when errors exist.
213      */
214     private String errorStyleClass = null;
215 
216     /***
217      * Identifier associated with component when errors exist.
218      */
219     private String errorStyleId = null;
220 
221     // Other Common Attributes
222 
223     /***
224      * The alternate text of this element.
225      */
226     private String alt = null;
227 
228     /***
229      * The message resources key of the alternate text.
230      */
231     private String altKey = null;
232 
233     /***
234      * The name of the message resources bundle for message lookups.
235      */
236     private String bundle = null;
237 
238     /***
239      * The name of the session attribute key for our locale.
240      */
241     private String locale = Globals.LOCALE_KEY;
242 
243     /***
244      * The advisory title of this element.
245      */
246     private String title = null;
247 
248     /***
249      * The message resources key of the advisory title.
250      */
251     private String titleKey = null;
252     private Class loopTagClass = null;
253     private Method loopTagGetStatus = null;
254     private Class loopTagStatusClass = null;
255     private Method loopTagStatusGetIndex = null;
256     private boolean triedJstlInit = false;
257     private boolean triedJstlSuccess = false;
258 
259     // ------------------------------------------------------------- Properties
260     //  Navigation Management
261 
262     /***
263      * Sets the accessKey character.
264      */
265     public void setAccesskey(String accessKey) {
266         this.accesskey = accessKey;
267     }
268 
269     /***
270      * Returns the accessKey character.
271      */
272     public String getAccesskey() {
273         return (this.accesskey);
274     }
275 
276     /***
277      * Sets the tabIndex value.
278      */
279     public void setTabindex(String tabIndex) {
280         this.tabindex = tabIndex;
281     }
282 
283     /***
284      * Returns the tabIndex value.
285      */
286     public String getTabindex() {
287         return (this.tabindex);
288     }
289 
290     //  Indexing ability for Iterate [since Struts 1.1]
291 
292     /***
293      * Sets the indexed value.
294      *
295      * @since Struts 1.1
296      */
297     public void setIndexed(boolean indexed) {
298         this.indexed = indexed;
299     }
300 
301     /***
302      * Returns the indexed value.
303      *
304      * @since Struts 1.1
305      */
306     public boolean getIndexed() {
307         return (this.indexed);
308     }
309 
310     // Mouse Events
311 
312     /***
313      * Sets the onClick event handler.
314      */
315     public void setOnclick(String onClick) {
316         this.onclick = onClick;
317     }
318 
319     /***
320      * Returns the onClick event handler.
321      */
322     public String getOnclick() {
323         return onclick;
324     }
325 
326     /***
327      * Sets the onDblClick event handler.
328      */
329     public void setOndblclick(String onDblClick) {
330         this.ondblclick = onDblClick;
331     }
332 
333     /***
334      * Returns the onDblClick event handler.
335      */
336     public String getOndblclick() {
337         return ondblclick;
338     }
339 
340     /***
341      * Sets the onMouseDown event handler.
342      */
343     public void setOnmousedown(String onMouseDown) {
344         this.onmousedown = onMouseDown;
345     }
346 
347     /***
348      * Returns the onMouseDown event handler.
349      */
350     public String getOnmousedown() {
351         return onmousedown;
352     }
353 
354     /***
355      * Sets the onMouseUp event handler.
356      */
357     public void setOnmouseup(String onMouseUp) {
358         this.onmouseup = onMouseUp;
359     }
360 
361     /***
362      * Returns the onMouseUp event handler.
363      */
364     public String getOnmouseup() {
365         return onmouseup;
366     }
367 
368     /***
369      * Sets the onMouseMove event handler.
370      */
371     public void setOnmousemove(String onMouseMove) {
372         this.onmousemove = onMouseMove;
373     }
374 
375     /***
376      * Returns the onMouseMove event handler.
377      */
378     public String getOnmousemove() {
379         return onmousemove;
380     }
381 
382     /***
383      * Sets the onMouseOver event handler.
384      */
385     public void setOnmouseover(String onMouseOver) {
386         this.onmouseover = onMouseOver;
387     }
388 
389     /***
390      * Returns the onMouseOver event handler.
391      */
392     public String getOnmouseover() {
393         return onmouseover;
394     }
395 
396     /***
397      * Sets the onMouseOut event handler.
398      */
399     public void setOnmouseout(String onMouseOut) {
400         this.onmouseout = onMouseOut;
401     }
402 
403     /***
404      * Returns the onMouseOut event handler.
405      */
406     public String getOnmouseout() {
407         return onmouseout;
408     }
409 
410     // Keyboard Events
411 
412     /***
413      * Sets the onKeyDown event handler.
414      */
415     public void setOnkeydown(String onKeyDown) {
416         this.onkeydown = onKeyDown;
417     }
418 
419     /***
420      * Returns the onKeyDown event handler.
421      */
422     public String getOnkeydown() {
423         return onkeydown;
424     }
425 
426     /***
427      * Sets the onKeyUp event handler.
428      */
429     public void setOnkeyup(String onKeyUp) {
430         this.onkeyup = onKeyUp;
431     }
432 
433     /***
434      * Returns the onKeyUp event handler.
435      */
436     public String getOnkeyup() {
437         return onkeyup;
438     }
439 
440     /***
441      * Sets the onKeyPress event handler.
442      */
443     public void setOnkeypress(String onKeyPress) {
444         this.onkeypress = onKeyPress;
445     }
446 
447     /***
448      * Returns the onKeyPress event handler.
449      */
450     public String getOnkeypress() {
451         return onkeypress;
452     }
453 
454     // Text Events
455 
456     /***
457      * Sets the onChange event handler.
458      */
459     public void setOnchange(String onChange) {
460         this.onchange = onChange;
461     }
462 
463     /***
464      * Returns the onChange event handler.
465      */
466     public String getOnchange() {
467         return onchange;
468     }
469 
470     /***
471      * Sets the onSelect event handler.
472      */
473     public void setOnselect(String onSelect) {
474         this.onselect = onSelect;
475     }
476 
477     /***
478      * Returns the onSelect event handler.
479      */
480     public String getOnselect() {
481         return onselect;
482     }
483 
484     // Focus Events and States
485 
486     /***
487      * Sets the onBlur event handler.
488      */
489     public void setOnblur(String onBlur) {
490         this.onblur = onBlur;
491     }
492 
493     /***
494      * Returns the onBlur event handler.
495      */
496     public String getOnblur() {
497         return onblur;
498     }
499 
500     /***
501      * Sets the onFocus event handler.
502      */
503     public void setOnfocus(String onFocus) {
504         this.onfocus = onFocus;
505     }
506 
507     /***
508      * Returns the onFocus event handler.
509      */
510     public String getOnfocus() {
511         return onfocus;
512     }
513 
514     /***
515      * Sets the disabled event handler.
516      */
517     public void setDisabled(boolean disabled) {
518         this.disabled = disabled;
519     }
520 
521     /***
522      * Returns the disabled event handler.
523      */
524     public boolean getDisabled() {
525         return disabled;
526     }
527 
528     /***
529      * Sets the readonly event handler.
530      */
531     public void setReadonly(boolean readonly) {
532         this.readonly = readonly;
533     }
534 
535     /***
536      * Returns the readonly event handler.
537      */
538     public boolean getReadonly() {
539         return readonly;
540     }
541 
542     // CSS Style Support
543 
544     /***
545      * Sets the style attribute.
546      */
547     public void setStyle(String style) {
548         this.style = style;
549     }
550 
551     /***
552      * Returns the style attribute.
553      */
554     public String getStyle() {
555         return style;
556     }
557 
558     /***
559      * Sets the style class attribute.
560      */
561     public void setStyleClass(String styleClass) {
562         this.styleClass = styleClass;
563     }
564 
565     /***
566      * Returns the style class attribute.
567      */
568     public String getStyleClass() {
569         return styleClass;
570     }
571 
572     /***
573      * Sets the style id attribute.
574      */
575     public void setStyleId(String styleId) {
576         this.styleId = styleId;
577     }
578 
579     /***
580      * Returns the style id attribute.
581      */
582     public String getStyleId() {
583         return styleId;
584     }
585 
586     /***
587      * Returns the error key attribute.
588      */
589     public String getErrorKey() {
590         return errorKey;
591     }
592 
593     /***
594      * Sets the error key attribute.
595      */
596     public void setErrorKey(String errorKey) {
597         this.errorKey = errorKey;
598     }
599 
600     /***
601      * Returns the error style attribute.
602      */
603     public String getErrorStyle() {
604         return errorStyle;
605     }
606 
607     /***
608      * Sets the error style attribute.
609      */
610     public void setErrorStyle(String errorStyle) {
611         this.errorStyle = errorStyle;
612     }
613 
614     /***
615      * Returns the error style class attribute.
616      */
617     public String getErrorStyleClass() {
618         return errorStyleClass;
619     }
620 
621     /***
622      * Sets the error style class attribute.
623      */
624     public void setErrorStyleClass(String errorStyleClass) {
625         this.errorStyleClass = errorStyleClass;
626     }
627 
628     /***
629      * Returns the error style id attribute.
630      */
631     public String getErrorStyleId() {
632         return errorStyleId;
633     }
634 
635     /***
636      * Sets the error style id attribute.
637      */
638     public void setErrorStyleId(String errorStyleId) {
639         this.errorStyleId = errorStyleId;
640     }
641 
642     // Other Common Elements
643 
644     /***
645      * Returns the alternate text attribute.
646      */
647     public String getAlt() {
648         return alt;
649     }
650 
651     /***
652      * Sets the alternate text attribute.
653      */
654     public void setAlt(String alt) {
655         this.alt = alt;
656     }
657 
658     /***
659      * Returns the message resources key of the alternate text.
660      */
661     public String getAltKey() {
662         return altKey;
663     }
664 
665     /***
666      * Sets the message resources key of the alternate text.
667      */
668     public void setAltKey(String altKey) {
669         this.altKey = altKey;
670     }
671 
672     /***
673      * Returns the name of the message resources bundle to use.
674      */
675     public String getBundle() {
676         return bundle;
677     }
678 
679     /***
680      * Sets the name of the message resources bundle to use.
681      */
682     public void setBundle(String bundle) {
683         this.bundle = bundle;
684     }
685 
686     /***
687      * Returns the name of the session attribute for our locale.
688      */
689     public String getLocale() {
690         return locale;
691     }
692 
693     /***
694      * Sets the name of the session attribute for our locale.
695      */
696     public void setLocale(String locale) {
697         this.locale = locale;
698     }
699 
700     /***
701      * Returns the advisory title attribute.
702      */
703     public String getTitle() {
704         return title;
705     }
706 
707     /***
708      * Sets the advisory title attribute.
709      */
710     public void setTitle(String title) {
711         this.title = title;
712     }
713 
714     /***
715      * Returns the message resources key of the advisory title.
716      */
717     public String getTitleKey() {
718         return titleKey;
719     }
720 
721     /***
722      * Sets the message resources key of the advisory title.
723      */
724     public void setTitleKey(String titleKey) {
725         this.titleKey = titleKey;
726     }
727 
728     // --------------------------------------------------------- Public Methods
729 
730     /***
731      * Release any acquired resources.
732      */
733     public void release() {
734         super.release();
735         accesskey = null;
736         alt = null;
737         altKey = null;
738         bundle = null;
739         errorKey = Globals.ERROR_KEY;
740         errorStyle = null;
741         errorStyleClass = null;
742         errorStyleId = null;
743         indexed = false;
744         locale = Globals.LOCALE_KEY;
745         onclick = null;
746         ondblclick = null;
747         onmouseover = null;
748         onmouseout = null;
749         onmousemove = null;
750         onmousedown = null;
751         onmouseup = null;
752         onkeydown = null;
753         onkeyup = null;
754         onkeypress = null;
755         onselect = null;
756         onchange = null;
757         onblur = null;
758         onfocus = null;
759         disabled = false;
760         readonly = false;
761         style = null;
762         styleClass = null;
763         styleId = null;
764         tabindex = null;
765         title = null;
766         titleKey = null;
767     }
768 
769     // ------------------------------------------------------ Protected Methods
770 
771     /***
772      * Return the text specified by the literal value or the message resources
773      * key, if any; otherwise return <code>null</code>.
774      *
775      * @param literal Literal text value or <code>null</code>
776      * @param key     Message resources key or <code>null</code>
777      * @throws JspException if both arguments are non-null
778      */
779     protected String message(String literal, String key)
780         throws JspException {
781         if (literal != null) {
782             if (key != null) {
783                 JspException e =
784                     new JspException(messages.getMessage("common.both"));
785 
786                 TagUtils.getInstance().saveException(pageContext, e);
787                 throw e;
788             } else {
789                 return (literal);
790             }
791         } else {
792             if (key != null) {
793                 return TagUtils.getInstance().message(pageContext, getBundle(),
794                     getLocale(), key);
795             } else {
796                 return null;
797             }
798         }
799     }
800 
801     private Integer getJstlLoopIndex() {
802         if (!triedJstlInit) {
803             triedJstlInit = true;
804 
805             try {
806                 loopTagClass =
807                     RequestUtils.applicationClass(
808                         "javax.servlet.jsp.jstl.core.LoopTag");
809 
810                 loopTagGetStatus =
811                     loopTagClass.getDeclaredMethod("getLoopStatus", null);
812 
813                 loopTagStatusClass =
814                     RequestUtils.applicationClass(
815                         "javax.servlet.jsp.jstl.core.LoopTagStatus");
816 
817                 loopTagStatusGetIndex =
818                     loopTagStatusClass.getDeclaredMethod("getIndex", null);
819 
820                 triedJstlSuccess = true;
821             } catch (ClassNotFoundException ex) {
822                 // These just mean that JSTL isn't loaded, so ignore
823             } catch (NoSuchMethodException ex) {
824             }
825         }
826 
827         if (triedJstlSuccess) {
828             try {
829                 Object loopTag =
830                     findAncestorWithClass(this, loopTagClass);
831 
832                 if (loopTag == null) {
833                     return null;
834                 }
835 
836                 Object status = loopTagGetStatus.invoke(loopTag, null);
837 
838                 return (Integer) loopTagStatusGetIndex.invoke(status, null);
839             } catch (IllegalAccessException ex) {
840                 log.error(ex.getMessage(), ex);
841             } catch (IllegalArgumentException ex) {
842                 log.error(ex.getMessage(), ex);
843             } catch (InvocationTargetException ex) {
844                 log.error(ex.getMessage(), ex);
845             } catch (NullPointerException ex) {
846                 log.error(ex.getMessage(), ex);
847             } catch (ExceptionInInitializerError ex) {
848                 log.error(ex.getMessage(), ex);
849             }
850         }
851 
852         return null;
853     }
854 
855     /***
856      * Appends bean name with index in brackets for tags with 'true' value in
857      * 'indexed' attribute.
858      *
859      * @param handlers The StringBuffer that output will be appended to.
860      * @throws JspException if 'indexed' tag used outside of iterate tag.
861      */
862     protected void prepareIndex(StringBuffer handlers, String name)
863         throws JspException {
864         if (name != null) {
865             handlers.append(name);
866         }
867 
868         handlers.append("[");
869         handlers.append(getIndexValue());
870         handlers.append("]");
871 
872         if (name != null) {
873             handlers.append(".");
874         }
875     }
876 
877     /***
878      * Returns the index value for tags with 'true' value in 'indexed'
879      * attribute.
880      *
881      * @return the index value.
882      * @throws JspException if 'indexed' tag used outside of iterate tag.
883      */
884     protected int getIndexValue()
885         throws JspException {
886         // look for outer iterate tag
887         IterateTag iterateTag =
888             (IterateTag) findAncestorWithClass(this, IterateTag.class);
889 
890         if (iterateTag != null) {
891             return iterateTag.getIndex();
892         }
893 
894         // Look for JSTL loops
895         Integer i = getJstlLoopIndex();
896 
897         if (i != null) {
898             return i.intValue();
899         }
900 
901         // this tag should be nested in an IterateTag or JSTL loop tag, if it's not, throw exception
902         JspException e =
903             new JspException(messages.getMessage("indexed.noEnclosingIterate"));
904 
905         TagUtils.getInstance().saveException(pageContext, e);
906         throw e;
907     }
908 
909     /***
910      * Prepares the style attributes for inclusion in the component's HTML
911      * tag.
912      *
913      * @return The prepared String for inclusion in the HTML tag.
914      * @throws JspException if invalid attributes are specified
915      */
916     protected String prepareStyles()
917         throws JspException {
918         StringBuffer styles = new StringBuffer();
919 
920         boolean errorsExist = doErrorsExist();
921 
922         if (errorsExist && (getErrorStyleId() != null)) {
923             prepareAttribute(styles, "id", getErrorStyleId());
924         } else {
925             prepareAttribute(styles, "id", getStyleId());
926         }
927 
928         if (errorsExist && (getErrorStyle() != null)) {
929             prepareAttribute(styles, "style", getErrorStyle());
930         } else {
931             prepareAttribute(styles, "style", getStyle());
932         }
933 
934         if (errorsExist && (getErrorStyleClass() != null)) {
935             prepareAttribute(styles, "class", getErrorStyleClass());
936         } else {
937             prepareAttribute(styles, "class", getStyleClass());
938         }
939 
940         prepareAttribute(styles, "title", message(getTitle(), getTitleKey()));
941         prepareAttribute(styles, "alt", message(getAlt(), getAltKey()));
942 
943         return styles.toString();
944     }
945 
946     /***
947      * Determine if there are errors for the component.
948      *
949      * @return Whether errors exist.
950      */
951     protected boolean doErrorsExist()
952         throws JspException {
953         boolean errorsExist = false;
954 
955         if ((getErrorStyleId() != null) || (getErrorStyle() != null)
956             || (getErrorStyleClass() != null)) {
957             String actualName = prepareName();
958 
959             if (actualName != null) {
960                 ActionMessages errors =
961                     TagUtils.getInstance().getActionMessages(pageContext,
962                         errorKey);
963 
964                 errorsExist = ((errors != null)
965                     && (errors.size(actualName) > 0));
966             }
967         }
968 
969         return errorsExist;
970     }
971 
972     /***
973      * Prepares the actual name of the component.
974      *
975      * @return The actual component name.
976      */
977     protected String prepareName()
978         throws JspException {
979         return null;
980     }
981 
982     /***
983      * Prepares the event handlers for inclusion in the component's HTML tag.
984      *
985      * @return The prepared String for inclusion in the HTML tag.
986      */
987     protected String prepareEventHandlers() {
988         StringBuffer handlers = new StringBuffer();
989 
990         prepareMouseEvents(handlers);
991         prepareKeyEvents(handlers);
992         prepareTextEvents(handlers);
993         prepareFocusEvents(handlers);
994 
995         return handlers.toString();
996     }
997 
998     /***
999      * Prepares the mouse event handlers, appending them to the the given
1000      * StringBuffer.
1001      *
1002      * @param handlers The StringBuffer that output will be appended to.
1003      */
1004     protected void prepareMouseEvents(StringBuffer handlers) {
1005         prepareAttribute(handlers, "onclick", getOnclick());
1006         prepareAttribute(handlers, "ondblclick", getOndblclick());
1007         prepareAttribute(handlers, "onmouseover", getOnmouseover());
1008         prepareAttribute(handlers, "onmouseout", getOnmouseout());
1009         prepareAttribute(handlers, "onmousemove", getOnmousemove());
1010         prepareAttribute(handlers, "onmousedown", getOnmousedown());
1011         prepareAttribute(handlers, "onmouseup", getOnmouseup());
1012     }
1013 
1014     /***
1015      * Prepares the keyboard event handlers, appending them to the the given
1016      * StringBuffer.
1017      *
1018      * @param handlers The StringBuffer that output will be appended to.
1019      */
1020     protected void prepareKeyEvents(StringBuffer handlers) {
1021         prepareAttribute(handlers, "onkeydown", getOnkeydown());
1022         prepareAttribute(handlers, "onkeyup", getOnkeyup());
1023         prepareAttribute(handlers, "onkeypress", getOnkeypress());
1024     }
1025 
1026     /***
1027      * Prepares the text event handlers, appending them to the the given
1028      * StringBuffer.
1029      *
1030      * @param handlers The StringBuffer that output will be appended to.
1031      */
1032     protected void prepareTextEvents(StringBuffer handlers) {
1033         prepareAttribute(handlers, "onselect", getOnselect());
1034         prepareAttribute(handlers, "onchange", getOnchange());
1035     }
1036 
1037     /***
1038      * Prepares the focus event handlers, appending them to the the given
1039      * StringBuffer.
1040      *
1041      * @param handlers The StringBuffer that output will be appended to.
1042      */
1043     protected void prepareFocusEvents(StringBuffer handlers) {
1044         prepareAttribute(handlers, "onblur", getOnblur());
1045         prepareAttribute(handlers, "onfocus", getOnfocus());
1046 
1047         // Get the parent FormTag (if necessary)
1048         FormTag formTag = null;
1049 
1050         if ((doDisabled && !getDisabled()) || (doReadonly && !getReadonly())) {
1051             formTag =
1052                 (FormTag) pageContext.getAttribute(Constants.FORM_KEY,
1053                     PageContext.REQUEST_SCOPE);
1054         }
1055 
1056         // Format Disabled
1057         if (doDisabled) {
1058             boolean formDisabled =
1059                 (formTag == null) ? false : formTag.isDisabled();
1060 
1061             if (formDisabled || getDisabled()) {
1062                 handlers.append(" disabled=\"disabled\"");
1063             }
1064         }
1065 
1066         // Format Read Only
1067         if (doReadonly) {
1068             boolean formReadOnly =
1069                 (formTag == null) ? false : formTag.isReadonly();
1070 
1071             if (formReadOnly || getReadonly()) {
1072                 handlers.append(" readonly=\"readonly\"");
1073             }
1074         }
1075     }
1076 
1077     /***
1078      * 'Hook' to enable tags to be extended and additional attributes added.
1079      *
1080      * @param handlers The StringBuffer that output will be appended to.
1081      */
1082     protected void prepareOtherAttributes(StringBuffer handlers) {
1083     }
1084 
1085     /***
1086      * Prepares an attribute if the value is not null, appending it to the the
1087      * given StringBuffer.
1088      *
1089      * @param handlers The StringBuffer that output will be appended to.
1090      */
1091     protected void prepareAttribute(StringBuffer handlers, String name,
1092         Object value) {
1093         if (value != null) {
1094             handlers.append(" ");
1095             handlers.append(name);
1096             handlers.append("=\"");
1097             handlers.append(value);
1098             handlers.append("\"");
1099         }
1100     }
1101 
1102     /***
1103      * Allows HTML tags to find out if they're nested within an
1104      * %lt;html:html&gt; tag that has xhtml set to true.
1105      *
1106      * @return true if the tag is nested within an html tag with xhtml set to
1107      *         true, false otherwise.
1108      * @since Struts 1.1
1109      */
1110     protected boolean isXhtml() {
1111         return TagUtils.getInstance().isXhtml(this.pageContext);
1112     }
1113 
1114     /***
1115      * Returns the closing brace for an input element depending on xhtml
1116      * status.  The tag must be nested within an %lt;html:html&gt; tag that
1117      * has xhtml set to true.
1118      *
1119      * @return String - &gt; if xhtml is false, /&gt; if xhtml is true
1120      * @since Struts 1.1
1121      */
1122     protected String getElementClose() {
1123         return this.isXhtml() ? " />" : ">";
1124     }
1125 
1126     /***
1127      * Searches all scopes for the bean and calls BeanUtils.getProperty() with
1128      * the given arguments and converts any exceptions into JspException.
1129      *
1130      * @param beanName The name of the object to get the property from.
1131      * @param property The name of the property to get.
1132      * @return The value of the property.
1133      * @throws JspException
1134      * @since Struts 1.1
1135      */
1136     protected String lookupProperty(String beanName, String property)
1137         throws JspException {
1138         Object bean =
1139             TagUtils.getInstance().lookup(this.pageContext, beanName, null);
1140 
1141         if (bean == null) {
1142             throw new JspException(messages.getMessage("getter.bean", beanName));
1143         }
1144 
1145         try {
1146             return BeanUtils.getProperty(bean, property);
1147         } catch (IllegalAccessException e) {
1148             throw new JspException(messages.getMessage("getter.access",
1149                     property, beanName));
1150         } catch (InvocationTargetException e) {
1151             Throwable t = e.getTargetException();
1152 
1153             throw new JspException(messages.getMessage("getter.result",
1154                     property, t.toString()));
1155         } catch (NoSuchMethodException e) {
1156             throw new JspException(messages.getMessage("getter.method",
1157                     property, beanName));
1158         }
1159     }
1160 }