View Javadoc

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