View Javadoc

1   /*
2    * Copyright (c) 2001 - 2005 ivata limited.
3    * All rights reserved.
4    * -----------------------------------------------------------------------------
5    * ivata masks may be redistributed under the GNU General Public
6    * License as published by the Free Software Foundation;
7    * version 2 of the License.
8    *
9    * These programs are free software; you can redistribute them and/or
10   * modify them under the terms of the GNU General Public License
11   * as published by the Free Software Foundation; version 2 of the License.
12   *
13   * These programs are distributed in the hope that they will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16   *
17   * See the GNU General Public License in the file LICENSE.txt for more
18   * details.
19   *
20   * If you would like a copy of the GNU General Public License write to
21   *
22   * Free Software Foundation, Inc.
23   * 59 Temple Place - Suite 330
24   * Boston, MA 02111-1307, USA.
25   *
26   *
27   * To arrange commercial support and licensing, contact ivata at
28   *                  http://www.ivata.com/contact.jsp
29   * -----------------------------------------------------------------------------
30   * $Log: MaskProperties.java,v $
31   * Revision 1.6  2005/04/28 18:23:35  colinmacleod
32   * Added server/contextPath rewriting.
33   *
34   * Revision 1.5  2005/04/11 15:25:56  colinmacleod
35   * Added handling for hidden tags.
36   *
37   * Revision 1.4  2005/04/09 18:04:20  colinmacleod
38   * Changed copyright text to GPL v2 explicitly.
39   *
40   * Revision 1.3  2005/03/10 10:45:38  colinmacleod
41   * Fixes for tomcat4. Changed setters and getters
42   * to both use the same types.
43   *
44   * Revision 1.2  2005/01/19 13:14:04  colinmacleod
45   * Renamed CausedByException to SystemException.
46   *
47   * Revision 1.1  2005/01/06 23:10:06  colinmacleod
48   * Moved up a version number.
49   * Changed copyright notices to 2005.
50   * Updated the documentation:
51   *   - started working on multiproject:site docu.
52   *   - changed the logo.
53   * Added checkstyle and fixed LOADS of style issues.
54   * Added separate thirdparty subproject.
55   * Added struts (in web), util and webgui (in webtheme) from ivata op.
56   *
57   * Revision 1.8  2004/12/27 14:52:02  colinmacleod
58   * Removed unused references to RequestUtils.
59   * Updated other references to Struts 1.2.4 class TagUtils.
60   *
61   * Revision 1.7  2004/12/23 21:01:31  colinmacleod
62   * Updated Struts to v1.2.4.
63   * Changed base classes to use ivata masks.
64   *
65   * Revision 1.6  2004/11/12 15:57:21  colinmacleod
66   * Removed dependencies on SSLEXT.
67   * Moved Persistence classes to ivata masks.
68   *
69   * Revision 1.5  2004/11/03 16:11:28  colinmacleod
70   * Added debug logging for paths.
71   *
72   * Revision 1.4  2004/07/13 19:48:10  colinmacleod
73   * Moved project to POJOs from EJBs.
74   * Applied PicoContainer to services layer (replacing session EJBs).
75   * Applied Hibernate to persistence layer (replacing entity EJBs).
76   *
77   * Revision 1.3  2004/03/21 21:16:35  colinmacleod
78   * Shortened name to ivata op.
79   *
80   * Revision 1.2  2004/02/04 23:36:47  colinmacleod
81   * Made class public
82   *
83   * Revision 1.1.1.1  2004/01/27 20:59:40  colinmacleod
84   * Moved ivata op to SourceForge.
85   *
86   * Revision 1.2  2003/10/16 15:43:03  jano
87   * Fixes problems with building and some problems with splitting to subprojects
88   *
89   * Revision 1.1.1.1  2003/10/13 20:49:26  colin
90   * Restructured portal into subprojects
91   *
92   * Revision 1.13  2003/06/10 06:07:40  peter
93   * ImgTag added
94   *
95   * Revision 1.12  2003/06/02 23:47:13  colin
96   * added multibox tag
97   *
98   * Revision 1.11  2003/04/27 19:12:19  peter
99   * the source was broken, regenerated
100  *
101  * Revision 1.10  2003/04/25 17:24:24  peter
102  * added defaultButton JavaScript for input tags
103  *
104  * Revision 1.9  2003/03/04 17:51:12  colin
105  * made local copies of keys in doStartTag
106  *
107  * Revision 1.8  2003/03/04 00:18:51  colin
108  * added resourceFieldPath to label directly
109  *
110  * Revision 1.7  2003/03/03 20:52:43  colin
111  * null fieldName now allowed for button
112  *
113  * Revision 1.6  2003/03/03 19:05:23  colin
114  * made checking for labelKeySuffix null or empty (was just null checking)
115  *
116  * Revision 1.5  2003/03/03 18:46:06  colin
117  * moved masking of some tags from MaskProperties to IFrameTag
118  *
119  * Revision 1.4  2003/03/03 17:51:23  colin
120  * bug in resourceFieldPath.toString()
121  *
122  * Revision 1.3  2003/03/03 17:49:56  colin
123  * fixed error messages - StringBuffer append
124  *
125  * Revision 1.2  2003/03/03 16:57:12  colin
126  * converted localization to automatic paths
127  * added labels
128  * added mandatory fieldName attribute
129  *
130  * Revision 1.1  2003/02/24 19:33:33  colin
131  * Moved to new subproject.
132  *
133  * Revision 1.4  2003/02/24 09:49:26  colin
134  * added arguments for i18n keys
135  *
136  * Revision 1.2  2003/02/13 08:51:06  colin
137  * added doStartTag method to centralize ivata maskTag
138  * initialization
139  *
140  * Revision 1.1  2003/01/18 20:32:08  colin
141  * added HTML struts override tags and help
142  * -----------------------------------------------------------------------------
143  */
144 package com.ivata.mask.web.tag.html;
145 import java.lang.reflect.InvocationTargetException;
146 import java.util.HashMap;
147 import java.util.Iterator;
148 import java.util.List;
149 import java.util.Locale;
150 import java.util.Map;
151 import java.util.Set;
152 import java.util.StringTokenizer;
153 import java.util.Vector;
154 
155 import javax.servlet.jsp.JspException;
156 import javax.servlet.jsp.PageContext;
157 import javax.servlet.jsp.tagext.TagSupport;
158 
159 import org.apache.commons.beanutils.PropertyUtils;
160 import org.apache.log4j.Logger;
161 import org.apache.struts.Globals;
162 import org.apache.struts.taglib.html.BaseHandlerTag;
163 
164 import com.ivata.mask.util.StringHandling;
165 import com.ivata.mask.util.SystemException;
166 import com.ivata.mask.web.struts.util.MessageResourcesHandling;
167 /***
168  * <p>
169  * This is the implementation of routines shared across the <strong>Struts
170  * </strong> override tags. It originally appeared in <strong>ivata op
171  * </strong>.
172  * </p>
173  *
174  * <p>
175  * One of the functions of these properties is to automate message resource
176  * paths. If a label key, or a title is not specified for a tag, it is
177  * defaulted by searching for the key in the bundle specified and, if it is
178  * not found there, in the main bundle.
179  * </p>
180  * <p>
181  * This docu is not complete. For an example of the paths and how they work,
182  * please refer to the <code>ApplicationResources</code> file in the
183  * <strong>ivata masks</strong> demo.
184  * TODO: explain fully the path defaulting here...
185  * </p>
186  *
187  * @author Colin MacLeod
188  * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
189  * @since ivata masks 0.4 (2003-01-18)
190  */
191 public class MaskProperties {
192     /***
193      * Logger for this class.
194      */
195     private static Logger logger = Logger.getLogger(MaskProperties.class);
196     /***
197      * <p>
198      * If a bundle was set in the tag, this value is stored. Otherwise the value
199      * of <code>bundle</code> from the parent form tag is taken.
200      * </p>
201      */
202     private String bundle = null;
203     /***
204      * <p>
205      * In a label tag, you can specify a class for which to display the correct
206      * label.
207      * </p>
208      */
209     private String className = null;
210     /***
211      * <p>
212      * The field name is used to default the following attributes of the tag:
213      * <br/>
214      * <ul>
215      * <li><code>styleId</code></li>
216      * <li><code>titleKey</code></li>
217      * <li><code>valueKey</code></li>
218      * </ul>.
219      * </p>
220      *
221      * <p>
222      * The <code>valueKey</code> and <code>titleKey</code> are obtained
223      * by prefixing the value of <code>resourceFieldPath</code> from the
224      * surrounding
225      * form.</p>
226      */
227     private String fieldName = null;
228     /***
229      * <p>
230      * Contains any arguments to the label key.
231      * </p>
232      */
233     private List labelArgs = new Vector();
234     /***
235      * <p>
236      * Localization key of a string which will appear as a label, for example to
237      * the right of a check box.
238      * </p>
239      */
240     private String labelKey = null;
241     /***
242      * <p>
243      * Some fields have multiple label keys. This
244      * suffix is appended to the key when retrieving the localized text from the
245      * application resources file.
246      * </p>
247      */
248     private String labelKeySuffix = null;
249     /***
250      * <p>
251      * String representation of the current locale. Taken from the session in
252      * <code>doStartTag</code>.
253      * </p>
254      */
255     private String locale = null;
256     /***
257      * <p>
258      * <code>true</code> if this field must be entered, otherwise
259      * <code>false</code> if the field is optional.
260      * </p>
261      */
262     private boolean mandatory = false;
263     /***
264      * <p>
265      * Current <i>JSP </i> page context. Set in <code>doStartTag</code>.
266      * </p>
267      */
268     private PageContext pageContext;
269     /***
270      * <p>
271      * Stores a reference to the <code>FormTag</code> which surrounds this
272      * mask tag.
273      * </p>
274      */
275     private FormTag parentForm = null;
276     /***
277      * <p>
278      * If non- <code>null</code>, indicates whether or not this field can be
279      * changed by the user (<code>true</code> means it can be change,
280      * <code>false</code> means it cannot).</p>
281      *
282      * <p>If it is null, then the value is taken from the parent form
283      * properties.</p>
284      */
285     private Boolean readOnly = null;
286     /***
287      * <p>
288      * Contains any arguments to the title key.
289      * </p>
290      */
291     private List titleArgs = new Vector();
292     /***
293      * <p>
294      * Stores the value of the key used to localize the title tag attribute.
295      * </p>
296      */
297     private String titleKey = null;
298     /***
299      * <p>
300      * Contains any arguments to the value key.
301      * </p>
302      */
303     private List valueArgs = new Vector();
304     /***
305      * <p>
306      * Stores the value of the key used to localize the value tag attribute.
307      * </p>
308      */
309     private String valueKey = null;
310     /***
311      * First off you <strong>have</strong> to have a field name, unless this tag
312      * is a button, hidden or an image tag.
313      *
314      * @param maskTagParam current tag, for which to check the field name.
315      * @throws JspException if there is no field name, and this tag
316      * should have one. (Button tags and image tags need not have a
317      * field name.)
318      */
319     private void checkFieldName(final BaseHandlerTag maskTagParam)
320             throws JspException {
321         if ((fieldName == null)
322                 && (className == null)
323                 && !(FormTag.class.isInstance(maskTagParam)
324                         || ButtonTag.class.isInstance(maskTagParam)
325                         || HiddenTag.class.isInstance(maskTagParam)
326                         || ImgTag.class
327                         .isInstance(maskTagParam))) {
328             throw new JspException("ERROR in "
329                     + maskTagParam.getClass().getName()
330                     + ": fieldName is null.");
331         }
332     }
333     /***
334      * if there was no bundle set in the tag, gets the bundle from the form.
335      */
336     private void defaultBundle() {
337         if ((bundle == null)
338                 && (parentForm != null)) {
339             bundle = parentForm.getBundle();
340             if (logger.isDebugEnabled()) {
341                 logger.debug("Retrieving bundle '" + bundle
342                         + "' from parent form.");
343             }
344         }
345     }
346 
347     /***
348      * Contains all attribute values we changed, so we can change them back in
349      * <code>reset</code> (keeps <strong>Apache Tomcat</strong> (et al) happy).
350      */
351     Map propertiesToReset = new HashMap();
352     /***
353      * Called by each tag which uses these mask properties to initialize;
354      * usually called in <code>doStartTag</code> methods of the tags.
355      *
356      * @param maskTagParam the tag which contains these mask properties.
357      * @param pageContextParam current page context for the tag.
358      * @throws JspException if a method or property cannot be resolved, or if
359      * there is no surrounding form.
360      */
361     void doStartTag(final BaseHandlerTag maskTagParam,
362             final PageContext pageContextParam)
363             throws JspException {
364         String onKeyPress = StringHandling.getNotNull(maskTagParam
365                 .getOnkeypress(), "");
366         checkFieldName(maskTagParam);
367         getFormTag(maskTagParam);
368 
369         // clear any attributes to reset from the last time
370         propertiesToReset.clear();
371 
372         boolean button =
373             (SubmitTag.class.isInstance(maskTagParam)
374                     || ButtonTag.class.isInstance(maskTagParam)
375                     || CancelTag.class.isInstance(maskTagParam)
376                     || ResetTag.class
377                     .isInstance(maskTagParam));
378 
379         this.pageContext = pageContextParam;
380         Locale localeObject = (Locale) pageContextParam.getAttribute(
381                 Globals.LOCALE_KEY, PageContext.SESSION_SCOPE);
382         // TODO: this probably isn't enough - need country?
383         if (localeObject == null) {
384             logger.warn("Locale not found. Defaulting to english.");
385             locale = "en";
386         } else {
387             locale = localeObject.getLanguage();
388         }
389         defaultBundle();
390         String resourceFieldPathString = getDefaultResourceFieldPathString(
391                 maskTagParam);
392         String title;
393         try {
394             title = MessageResourcesHandling.getDefaultTitle(
395                     localeObject,
396                     bundle,
397                     fieldName,
398                     titleKey,
399                     titleArgs,
400                     resourceFieldPathString,
401                     button,
402                     button);
403         } catch (SystemException e1) {
404             throw new JspException(e1);
405         }
406         // TODO: there is a lot of duplication in the following bit.....
407         // if we found a title key, set the title
408         setProperty(maskTagParam, "title", title);
409         String value;
410         try {
411             value = MessageResourcesHandling.getDefaultValue(
412                     localeObject,
413                     bundle,
414                     fieldName,
415                     valueKey,
416                     valueArgs,
417                     resourceFieldPathString,
418                     button,
419                     button);
420         } catch (SystemException e2) {
421             throw new JspException(e2);
422         }
423         // if we found a value key, set the value
424         setProperty(maskTagParam, "value", value);
425         String label;
426         try {
427             String name = fieldName;
428             if (StringHandling.isNullOrEmpty(name)
429                     && !StringHandling.isNullOrEmpty(className)) {
430                 int nameStart = className.lastIndexOf('.');
431                 // either start at 0 (if not found), or one after the dot
432                 ++nameStart;
433                 int nameEnd = className.length();
434                 // FIXME: this could be made more general for any class suffix
435                 // format
436                 if (className.endsWith("DO")) {
437                     nameEnd -=2;
438                 }
439                 name = className.substring(nameStart, nameEnd);
440             }
441             label = MessageResourcesHandling.getDefaultLabel(
442                     localeObject,
443                     bundle,
444                     name,
445                     labelKey,
446                     labelArgs,
447                     resourceFieldPathString,
448                     labelKeySuffix,
449                     button,
450                     LabelTag.class.isInstance(maskTagParam));
451             // if this is a check box, that's a warning
452             if ((label == null) && CheckboxTag.class.isInstance(maskTagParam)) {
453                 logger.warn("Checkbox without label for field name '"
454                         + fieldName
455                         + "'");
456             }
457 
458         } catch (SystemException e3) {
459             throw new JspException(e3);
460         }
461         // if we found a label key, set the label
462         setProperty(maskTagParam, "label", label);
463         if (fieldName != null) {
464             // set the style Id if it is not set already (and it exists)
465             try {
466                 String styleId = (String) PropertyUtils.getSimpleProperty(
467                         maskTagParam, "styleId");
468                 // don't set the style id for radios - or we will get multiple
469                 // buttons with the same id
470                 if ((styleId == null)
471                         && !(maskTagParam instanceof RadioTag)) {
472                     setProperty(maskTagParam, "styleId", fieldName);
473                 }
474             } catch (NoSuchMethodException e) {
475                 // if this tag has no style id - no need to set it
476                 if (logger.isDebugEnabled()) {
477                     logger.debug("No styleId property in '"
478                             + maskTagParam.getId()
479                             + "'", e);
480                 }
481             } catch (IllegalAccessException e) {
482                 throw new JspException(e);
483             } catch (InvocationTargetException e) {
484                 throw new JspException(e);
485             }
486             // set the property if it is not set already (and it exists)
487             try {
488                 String property = (String) PropertyUtils.getSimpleProperty(
489                         maskTagParam, "property");
490                 if (property == null) {
491                     StringBuffer fieldPath = new StringBuffer();
492                     if ((parentForm != null)
493                             && (parentForm.getFieldPath() != null)) {
494                         fieldPath.append(parentForm.getFieldPath());
495                         fieldPath.append(".");
496                     }
497                     fieldPath.append(fieldName);
498                     setProperty(maskTagParam, "property", fieldPath.toString());
499                 }
500             } catch (NoSuchMethodException e) {
501                 // if this tag has no style id - no need to set it
502                 if (logger.isDebugEnabled()) {
503                     logger.debug("No property property in '"
504                             + maskTagParam.getId()
505                             + "'", e);
506                 }
507             } catch (IllegalAccessException e) {
508                 throw new JspException(e);
509             } catch (InvocationTargetException e) {
510                 throw new JspException(e);
511             }
512         }
513         IFrameTag iframeTag = (IFrameTag) TagSupport.findAncestorWithClass(
514                 maskTagParam, IFrameTag.class);
515         // defaultButton js handler
516         if (TextTag.class.isInstance(maskTagParam)
517                 || MultiboxTag.class.isInstance(maskTagParam)
518                 || CheckboxTag.class.isInstance(maskTagParam)
519                 || RadioTag.class.isInstance(maskTagParam)) {
520             assert (parentForm != null);
521             String formName;
522             if (parentForm.getReferredForm() == null) {
523                 formName = parentForm.getBeanName();
524             } else {
525                 formName = parentForm.getReferredForm().getBeanName();
526             }
527             if (onKeyPress.length() > 0 && (!onKeyPress.endsWith(";"))) {
528                 onKeyPress += ";";
529             }
530             String parent;
531             if (iframeTag == null) {
532                 parent = "";
533             } else {
534                 parent = "parent.";
535             }
536             onKeyPress += "return(" + parent + "checkEnter_" + formName
537                     + "(event));";
538             maskTagParam.setOnkeypress(onKeyPress);
539         }
540         modifyIfMandatory(maskTagParam);
541         // see if the mask tag is contained within an iframe and let the iframe
542         // know about it, if so
543         if (iframeTag != null) {
544             iframeTag.addMaskTag(maskTagParam, pageContextParam);
545         }
546         // escape quotes for all the JavaScript methods
547         maskTagParam.setOnblur(escapeQuotes(maskTagParam.getOnblur()));
548         maskTagParam.setOnchange(escapeQuotes(maskTagParam.getOnchange()));
549         maskTagParam.setOnclick(escapeQuotes(maskTagParam.getOnclick()));
550         maskTagParam.setOndblclick(escapeQuotes(maskTagParam.getOndblclick()));
551         maskTagParam.setOnfocus(escapeQuotes(maskTagParam.getOnfocus()));
552         maskTagParam.setOnkeydown(escapeQuotes(maskTagParam.getOnkeydown()));
553         maskTagParam.setOnkeypress(escapeQuotes(maskTagParam.getOnkeypress()));
554         maskTagParam.setOnkeyup(escapeQuotes(maskTagParam.getOnkeyup()));
555         maskTagParam
556                 .setOnmousedown(escapeQuotes(maskTagParam.getOnmousedown()));
557         maskTagParam
558                 .setOnmousemove(escapeQuotes(maskTagParam.getOnmousemove()));
559         maskTagParam.setOnmouseout(escapeQuotes(maskTagParam.getOnmouseout()));
560         maskTagParam
561                 .setOnmouseover(escapeQuotes(maskTagParam.getOnmouseover()));
562         maskTagParam.setOnmouseup(escapeQuotes(maskTagParam.getOnmouseup()));
563         maskTagParam.setOnselect(escapeQuotes(maskTagParam.getOnselect()));
564     }
565     /***
566      * <p>
567      * Helper method to escape double quote characters from JavaScript methods
568      * such as <code>onChange</code>.
569      * </p>
570      *
571      * @param escape
572      *            possibly <code>null</code> string to escape double quotes
573      *            for.
574      * @return string with all of the quotes escaped.
575      */
576     private String escapeQuotes(final String escape) {
577         if (escape == null) {
578             return null;
579         }
580         // TODO: for now this routine just replaces double quotes with
581         //  single ones - this won't work in cases where the two are mixed
582         StringTokenizer st = new StringTokenizer(escape, "\"");
583         StringBuffer buffer = new StringBuffer();
584         while (st.hasMoreElements()) {
585             // if this isn't he first element, prepend esacped quote
586             if (buffer.length() > 0) {
587                 buffer.append("'");
588             }
589             buffer.append(st.nextElement());
590         }
591         return buffer.toString();
592     }
593     /***
594      * <p>
595      * Set the bundle for this mask tag. The bundle is used to identify the
596      * set of message resources used to localize texts. See the
597      * <strong>Struts</strong> docu for more information.
598      * </p>
599      *
600      * @return text name of the message resources.
601      */
602     public final String getBundle() {
603         return bundle;
604     }
605     /***
606      * <p>
607      * In a label tag, you can specify a class for which to display the correct
608      * label.
609      * </p>
610      * @return Returns the className.
611      */
612     public String getClassName() {
613         return className;
614     }
615     /***
616      * If there was no <code>resourceFieldPath</code> set in the tag, gets it
617      * from the form.
618      *
619      * @param maskTagParam current tag, for which to find the default resource
620      * field path.
621      * @return valid resource field path for this tag.
622      * @throws JspException if there is no &quot;resourceFieldPath&quot;
623      * property in the tag or form.
624      */
625     private String getDefaultResourceFieldPathString(
626             final BaseHandlerTag maskTagParam)
627             throws JspException {
628         String resourceFieldPathString;
629         try {
630             resourceFieldPathString = (String) PropertyUtils.getSimpleProperty(
631                     maskTagParam, "resourceFieldPath");
632         } catch (NoSuchMethodException e) {
633             // get it from the form
634             resourceFieldPathString = null;
635         } catch (IllegalAccessException e) {
636             throw new JspException(e);
637         } catch (InvocationTargetException e) {
638             throw new JspException(e);
639         }
640         if ((resourceFieldPathString == null) && (parentForm != null)) {
641             resourceFieldPathString = parentForm.getResourceFieldPath();
642             if (logger.isDebugEnabled()) {
643                 logger.debug("Retrieving resource field path string '"
644                         + resourceFieldPathString + "' from parent form.");
645             }
646         }
647         return resourceFieldPathString;
648     }
649     /***
650      * <p>
651      * The field name is used to default the following attributes of the tag:
652      * <br/>
653      * <ul>
654      * <li><code>styleId</code></li>
655      * <li><code>titleKey</code></li>
656      * <li><code>valueKey</code></li>
657      * </ul>.
658      * </p>
659      *
660      * <p>
661      * The <code>valueKey</code> and <code>titleKey</code> are obtained
662      * by prefixing the value of <code>resourceFieldPath</code> from the
663      * surrounding
664      * form.</p>
665      *
666      * @return the current value of fieldName.
667      */
668     public String getFieldName() {
669         return fieldName;
670     }
671     /***
672      * Get and set the mask properties form tag for the tag provided.
673      *
674      * @param maskTagParam current tag, for which to find the form tag.
675      * @throws JspException if there is no surrounding form tag, and this tag
676      * should have one. (Button tags, labels and image tags need not have a
677      * surrounding form.)
678      */
679     private void getFormTag(final BaseHandlerTag maskTagParam)
680             throws JspException {
681         parentForm = (FormTag) TagSupport.findAncestorWithClass(maskTagParam,
682                 FormTag.class);
683         // if there is no form tag, complain - doesn't apply to buttons, images
684         // or labels
685         if ((parentForm == null)
686                 && !(ButtonTag.class.isInstance(maskTagParam)
687                         || LabelTag.class.isInstance(maskTagParam)
688                         || ImgTag.class
689                         .isInstance(maskTagParam))) {
690             throw new JspException("ERROR in "
691                     + maskTagParam.getClass().getName()
692                     + ": there is no surrounding form.");
693         }
694     }
695     /***
696      * <p>
697      * Contains any arguments to the label key.
698      * </p>
699      *
700      * @return the current value of labelArgs.
701      */
702     public List getLabelArgs() {
703         return labelArgs;
704     }
705     /***
706      * <p>
707      * Localization key of a string which will appear as a label, for example to
708      * the right of a check box.
709      * </p>
710      *
711      * @return the current value of labelKey.
712      */
713     public String getLabelKey() {
714         return labelKey;
715     }
716     /***
717      * <p>
718      * Some fields have multiple label keys. This
719      * suffix is appended to the key when retrieving the localized text from the
720      * application resources file.
721      * </p>
722      *
723      * @return the current value of labelKeySuffix.
724      */
725     public String getLabelKeySuffix() {
726         return labelKeySuffix;
727     }
728     /***
729      * <p>
730      * String representation of the current locale. Taken from the session in
731      * <code>doStartTag</code>.
732      * </p>
733      *
734      * @return the current value of locale.
735      */
736     public String getLocale() {
737         return locale;
738     }
739     /***
740      * <p>
741      * Get whether or not the value this control must be entered.
742      * </p>
743      *
744      * @return <code>true</code> if this field must be entered, otherwise
745      *         <code>false</code> if the field is optional.
746      */
747     public boolean getMandatory() {
748         return mandatory;
749     }
750     /***
751      * <p>
752      * Current <i>JSP </i> page context. Set in <code>doStartTag</code>.
753      * </p>
754      *
755      * @return the current value of pageContext.
756      */
757     public PageContext getPageContext() {
758         return pageContext;
759     }
760     /***
761      * <p>
762      * Stores a reference to the <code>FormTag</code> which surrounds this
763      * mask tagf.
764      * </p>
765      *
766      * @return the current value of parentForm.
767      */
768     public FormTag getParentForm() {
769         return parentForm;
770     }
771     /***
772      * <p>
773      * Specifies whether or not the input values can be altered.
774      * </p>
775      *
776      * <p>
777      * The handling here has been extended to take into account the state of the
778      * parent form. If the read only state of this tag has not been set
779      * explicitly (by {@link #setReadOnly(boolean readOnly)}, then we get the
780      * read only state of the parent {@link FormTag formtag} and return that.
781      * </p>
782      *
783      * @return <code>true</code> if the input of this field can be changed,
784      *         otherwise <code>false</code> to only display the current value.
785      */
786     public boolean getReadOnly() {
787         if (readOnly == null) {
788             if ((parentForm == null)
789                     || (parentForm.getMaskProperties().readOnly == null)) {
790                 return false;
791             }
792             return parentForm.getMaskProperties().readOnly.booleanValue();
793         }
794         return readOnly.booleanValue();
795     }
796     /***
797      * <p>
798      * Get the value of the title for this tag.
799      * </p>
800      *
801      * @return the current value of title.
802      * @throws JspException
803      *             thrown by {@link #getMessage getMessage}.
804      */
805     public String getTitle() throws JspException {
806         if (titleKey == null) {
807             return null;
808         }
809         try {
810             return MessageResourcesHandling.getMessage(getBundle(),
811                     locale, titleKey, titleArgs);
812         } catch (SystemException e) {
813             throw new JspException(e);
814         }
815     }
816     /***
817      * <p>
818      * Contains any arguments to the title key.
819      * </p>
820      *
821      * @return the current value of titleArgs.
822      */
823     public List getTitleArgs() {
824         return titleArgs;
825     }
826     /***
827      * <p>
828      * Stores the value of the key used to localize the title tag attribute.
829      * </p>
830      *
831      * @return the current value of titleKey.
832      */
833     public String getTitleKey() {
834         return titleKey;
835     }
836     /***
837      * <p>
838      * Get the localized value text from the value key and arguments.
839      * </p>
840      *
841      * @throws JspException
842      *             thrown by {@link #getMessage getMessage}.
843      * @return localized text representing <code>valueKey</code>.
844      */
845     public String getValue() throws JspException {
846         if (valueKey == null) {
847             return null;
848         }
849         try {
850             return MessageResourcesHandling.getMessage(getBundle(),
851                     locale, valueKey, valueArgs);
852         } catch (SystemException e) {
853             throw new JspException(e);
854         }
855     }
856     /***
857      * <p>
858      * Contains any arguments to the value key.
859      * </p>
860      *
861      * @return the current value of valueArgs.
862      */
863     public List getValueArgs() {
864         return valueArgs;
865     }
866     /***
867      * <p>
868      * Stores the value of the key used to localize the value tag attribute.
869      * </p>
870      *
871      * @return the current value of valueKey.
872      */
873     public String getValueKey() {
874         return valueKey;
875     }
876     /***
877      * <p>
878      * Modified the <strong>Struts </strong> control provided on the basis of
879      * whether or not it is mandatory.
880      * </p>
881      *
882      * @param control
883      *            <strong>Struts </strong> tag instance to be modified on the
884      *            basis of whether or not it is mandatory.
885      */
886     public void modifyIfMandatory(final BaseHandlerTag control) {
887         // for now, just modify the CSS class
888         if (mandatory) {
889             control.setStyleClass("mandatory");
890         }
891     }
892     /***
893      * <p>
894      * Reset the attributes and mask properties of the supplied tag. This method
895      * should be called after all processing on the tag, as the tag instances
896      * are re-used by the servlet engine. Calling this method ensures no two
897      * tags sharing the same instance receive the same attributes.
898      * </p>
899      *
900      * @param maskTag the tag whose properties should be reset.
901      * @throws JspException if there is an error accessing methods to reset,
902      * using <code>PropertyUtils.setSimpleProperty</code>.
903      */
904     public void reset(final BaseHandlerTag maskTag) throws JspException {
905         // try to reset the style id, property, title and value on the tag
906         // set the property if it is not set already (and it exists)
907         try {
908             Set propertyNames = propertiesToReset.keySet();
909             Iterator propertyNameIterator = propertyNames.iterator();
910             while (propertyNameIterator.hasNext()) {
911                 String propertyName = (String) propertyNameIterator.next();
912                 Object value = propertiesToReset.get(propertyName);
913                 PropertyUtils.setSimpleProperty(maskTag,
914                         propertyName, value);
915             }
916         } catch (NoSuchMethodException e) {
917             throw new JspException(e);
918         } catch (IllegalAccessException e) {
919             throw new JspException(e);
920         } catch (InvocationTargetException e) {
921             throw new JspException(e);
922         }
923     }
924     /***
925      * Refer to {@link #getBundle}.
926      * @param bundleParam Refer to {@link #getBundle}.
927      */
928     public void setBundle(final String bundleParam) {
929         if (logger.isDebugEnabled()) {
930             logger.debug("Setting bundle. Before '" + bundle
931                     + "', after '" + bundleParam + "'");
932         }
933         this.bundle = bundleParam;
934     }
935     /***
936      * Refer to {@link #getClassName}.
937      * @param classNameParam Refer to {@link #getClassName}.
938      */
939     public void setClassName(String classNameParam) {
940         if (logger.isDebugEnabled()) {
941             logger.debug("Setting className. Before '" + className
942                     + "', after '" + classNameParam + "'");
943         }
944         className = classNameParam;
945     }
946     /***
947      * <p>
948      * The field name is used to default the following attributes of the tag:
949      * <br/>
950      * <ul>
951      * <li><code>styleId</code></li>
952      * <li><code>titleKey</code></li>
953      * <li><code>valueKey</code></li>
954      * </ul>.
955      * </p>
956      *
957      * <p>
958      * The <code>valueKey</code> and <code>titleKey</code> are obtained
959      * by prefixing the value of <code>resourceFieldPath</code> from the
960      * surrounding
961      * form.</p>
962      *
963      * @param fieldNameParam the new value of fieldName.
964      */
965     public void setFieldName(final String fieldNameParam) {
966         if (logger.isDebugEnabled()) {
967             logger.debug("Setting fieldName. Before '" + fieldName
968                     + "', after '" + fieldNameParam + "'");
969         }
970         this.fieldName = fieldNameParam;
971     }
972     /***
973      * Set the arguments for the label message, as an array of strings.
974      *
975      * @param args
976      *            new value of the message argument.
977      */
978     public void setLabelArgs(final List args) {
979         if (logger.isDebugEnabled()) {
980             logger.debug("Setting labelArgs. Before '" + labelArgs
981                     + "', after '" + args + "'");
982         }
983         labelArgs = args;
984     }
985     /***
986      * <p>
987      * Localization key of a string which will appear as a label, for example to
988      * the right of a check box.
989      * </p>
990      *
991      * @param labelKeyParam
992      *            the new value of labelKey.
993      */
994     public final void setLabelKey(final String labelKeyParam) {
995         if (logger.isDebugEnabled()) {
996             logger.debug("Setting labelKey. Before '" + labelKey
997                     + "', after '" + labelKeyParam + "'");
998         }
999         this.labelKey = labelKeyParam;
1000     }
1001     /***
1002      * <p>
1003      * Some fields have multiple label keys. This
1004      * suffix is appended to the key when retrieving the localized text from the
1005      * application resources file.
1006      * </p>
1007      *
1008      * @param labelKeySuffixParam the new value of labelKeySuffix.
1009      */
1010     public final void setLabelKeySuffix(final String labelKeySuffixParam) {
1011         if (logger.isDebugEnabled()) {
1012             logger.debug("Setting labelKeySuffix. Before '" + labelKeySuffix
1013                     + "', after '" + labelKeySuffixParam + "'");
1014         }
1015         this.labelKeySuffix = labelKeySuffixParam;
1016     }
1017     /***
1018      * <p>
1019      * String representation of the current locale. Taken from the session in
1020      * <code>doStartTag</code>.
1021      * </p>
1022      *
1023      * @param localeParam the new value of locale.
1024      */
1025     public void setLocale(final String localeParam) {
1026         if (logger.isDebugEnabled()) {
1027             logger.debug("Setting localeParam. Before '" + locale
1028                     + "', after '" + localeParam + "'");
1029         }
1030         this.locale = localeParam;
1031     }
1032     /***
1033      * <p>
1034      * Set whether or not the value this control must be entered.
1035      * </p>
1036      *
1037      * @param mandatoryParam
1038      *            <code>true</code> if this field must be entered, otherwise
1039      *            <code>false</code> if the field is optional.
1040      */
1041     public void setMandatory(final boolean mandatoryParam) {
1042         if (logger.isDebugEnabled()) {
1043             logger.debug("Setting mandatory. Before '" + mandatory
1044                     + "', after '" + mandatoryParam + "'");
1045         }
1046         this.mandatory = mandatoryParam;
1047     }
1048     /***
1049      * <p>
1050      * Current <i>JSP </i> page context. Set in <code>doStartTag</code>.
1051      * </p>
1052      *
1053      * @param pageContextParam
1054      *            the new value of pageContext.
1055      */
1056     public void setPageContext(final PageContext pageContextParam) {
1057         if (logger.isDebugEnabled()) {
1058             logger.debug("Setting pageContext. Before '" + pageContext
1059                     + "', after '" + pageContextParam + "'");
1060         }
1061         this.pageContext = pageContextParam;
1062     }
1063     /***
1064      * <p>
1065      * Stores a reference to the <code>FormTag</code> which surrounds this
1066      * mask tagf.
1067      * </p>
1068      *
1069      * @param parentFormParam
1070      *            the new value of parentForm.
1071      */
1072     public void setParentForm(final FormTag parentFormParam) {
1073         if (logger.isDebugEnabled()) {
1074             logger.debug("Setting parentForm. Before '" + parentForm
1075                     + "', after '" + parentFormParam + "'");
1076         }
1077         this.parentForm = parentFormParam;
1078     }
1079     /***
1080      * Wrapper for <code>PropertyUtils.setProperty</code>, to handle exceptions,
1081      * and to mark the properties to reset in the <code>reset</code> method.
1082      *
1083      * @param maskTagParam the tag which contains this property.
1084      * @param propertyName property to set.
1085      * @param value value to set the property to. If <code>null</code>, then
1086      * nothing is done.
1087      * @throws JspException wraps any exception thrown by
1088      * <code>PropertyUtils.setProperty</code>.
1089      */
1090     private void setProperty(final BaseHandlerTag maskTagParam,
1091             final String propertyName, final String value)
1092             throws JspException {
1093         if (value == null) {
1094             return;
1095         }
1096         try {
1097             Object oldValue = PropertyUtils.getSimpleProperty(maskTagParam,
1098                     propertyName);
1099             PropertyUtils.setSimpleProperty(maskTagParam, propertyName,
1100                     value);
1101             propertiesToReset.put(propertyName, oldValue);
1102         } catch (NoSuchMethodException e) {
1103             if (logger.isDebugEnabled()) {
1104                 logger.debug("No such method for property '"
1105                         + propertyName
1106                         + "' in tag '"
1107                         +  StringHandling.getNotNull(
1108                                 maskTagParam.getClass().getName(),
1109                                 "[none]")
1110                         + "'", e);
1111             }
1112         } catch (Exception e) {
1113             logger.error("Could not set property '"
1114                     + propertyName
1115                     + "' in tag '"
1116                     +  StringHandling.getNotNull(
1117                             maskTagParam.getClass().getName(),
1118                             "[none]")
1119                     + "'", e);
1120             throw new JspException(e);
1121         }
1122     }
1123     /***
1124      * <p>
1125      * Specifies whether or not the input values can be altered.
1126      * </p>
1127      *
1128      * @param readOnlyParam
1129      *            <code>true</code> if the input of this field can be changed,
1130      *            otherwise <code>false</code> to only display the current
1131      *            value.
1132      */
1133     public void setReadOnly(final boolean readOnlyParam) {
1134         if (logger.isDebugEnabled()) {
1135             logger.debug("Setting readOnly. Before '" + readOnly
1136                     + "', after '" + readOnlyParam + "'");
1137         }
1138         this.readOnly = new Boolean(readOnlyParam);
1139     }
1140     /***
1141      * Set the arguments for the title message.
1142      *
1143      * @param args new value of the title arguments.
1144      */
1145     public final void setTitleArgs(final List args) {
1146         if (logger.isDebugEnabled()) {
1147             logger.debug("Setting titleArgs. Before '" + titleArgs
1148                     + "', after '" + args + "'");
1149         }
1150         titleArgs = args;
1151     }
1152     /***
1153      * <p>
1154      * Stores the value of the key used to localize the title tag attribute.
1155      * </p>
1156      *
1157      * @param titleKeyParam
1158      *            the new value of titleKey.
1159      */
1160     public final void setTitleKey(final String titleKeyParam) {
1161         if (logger.isDebugEnabled()) {
1162             logger.debug("Setting titleKey. Before '" + titleKey
1163                     + "', after '" + titleKeyParam + "'");
1164         }
1165         this.titleKey = titleKeyParam;
1166     }
1167     /***
1168      * <p>
1169      * Contains any arguments to the value key.
1170      * </p>
1171      *
1172      * @param valueArgsParam
1173      *            the new value of valueArgs.
1174      */
1175     public final void setValueArgs(final List valueArgsParam) {
1176         if (logger.isDebugEnabled()) {
1177             logger.debug("Setting valueArgs. Before '" + valueArgs
1178                     + "', after '" + valueArgsParam + "'");
1179         }
1180         this.valueArgs = valueArgsParam;
1181     }
1182     /***
1183      * <p>
1184      * Stores the value of the key used to localize the value tag attribute.
1185      * </p>
1186      *
1187      * @param valueKeyParam
1188      *            the new value of valueKey.
1189      */
1190     public void setValueKey(final String valueKeyParam) {
1191         if (logger.isDebugEnabled()) {
1192             logger.debug("Setting valueKey. Before '" + valueKey
1193                     + "', after '" + valueKeyParam + "'");
1194         }
1195         this.valueKey = valueKeyParam;
1196     }
1197 }