1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package com.ivata.mask.web.struts.util;
44
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.Locale;
48 import java.util.Map;
49 import java.util.Vector;
50
51 import org.apache.log4j.Logger;
52 import org.apache.struts.taglib.TagUtils;
53 import org.apache.struts.util.MessageResources;
54
55 import com.ivata.mask.util.StringHandling;
56 import com.ivata.mask.util.SystemException;
57
58 /***
59 * Methods in this class wrap/extend the message resource system of
60 * <strong>Struts</strong>.
61 *
62 * @since ivata masks 0.5 (2005-01-20)
63 * @author Colin MacLeod
64 * <a href="mailto:colin.macleod@ivata.com">colin.macleod@ivata.com</a>
65 * @version $Revision: 1.3 $
66 */
67 public final class MessageResourcesHandling {
68 /***
69 * This is prepended to fields to get their message resource string.
70 */
71 public static String FIELD_PREFIX = "field.";
72 /***
73 * If <code>true</code>, then the field name is returned for fields which
74 * have not been defined.
75 */
76 private static boolean guessLabel = true;
77 /***
78 * Logger for this class.
79 */
80 private static Logger logger =
81 Logger.getLogger(MessageResourcesHandling.class);
82
83 /***
84 * Stores all of the message resources, indexed by bundle name or
85 * the vale of the field <code>NULL_BUNDLE</code> for the default bundle.
86 */
87 private static Map messageResourcesMap = new HashMap();
88 /***
89 * Name of the <code>null</code> bundle in the map.
90 */
91 private static final String NULL_BUNDLE = "null_bundle";
92 /***
93 * This is prepended to submit buttons to get their message resource string.
94 */
95 public static String SUBMIT_PREFIX = "submit.";
96 /***
97 * This string is appended to field names to find their titles in the
98 * message resource bundle.
99 */
100 public static String TITLE_SUFFIX = ".title";
101 /***
102 * This string is appended to field names to find their values in the
103 * message resource bundle.
104 */
105 public static String VALUE_SUFFIX = ".value";
106 static {
107 MessageResourcesHandling.registerMessages(null,
108 "com.ivata.groupware.business.ApplicationResources");
109 MessageResourcesHandling.registerMessages("addressBook",
110 "com.ivata.groupware.business.addressbook.ApplicationResources");
111 MessageResourcesHandling.registerMessages("calendar",
112 "com.ivata.groupware.business.calendar.ApplicationResources");
113 MessageResourcesHandling.registerMessages("library",
114 "com.ivata.groupware.business.library.ApplicationResources");
115 MessageResourcesHandling.registerMessages("mail",
116 "com.ivata.groupware.business.mail.ApplicationResources");
117 MessageResourcesHandling.registerMessages("security",
118 "com.ivata.groupware.admin.security.ApplicationResources");
119 }
120 /***
121 * Get the default label for this field. If no label key is specifically
122 * set, the combination of
123 * <code>labelFieldPath + ".label"</code> is tried in the
124 * application resources. Failing that, the same is attempted using the
125 * default path (<code>defaultPath + ".label"</code>).
126 *
127 * @param locale Refer to {@link MessageResources#getMessage}.
128 * @param bundle Refer to {@link MessageResources#getMessage}.
129 * @param fieldName name of the field for which to return the title.
130 * @param labelKey key used to access the title in the resources. If this
131 * is <code>null</code>, a default key is tried.
132 * @param labelArgs arguments used in conjunction with the key, in the
133 * message resources.
134 * @param resourceFieldPath this is the root of the message resource path
135 * searched. This is usually specific to an input mask, or a value object.
136 * @param labelKeySuffix Some fields have multiple label keys. This
137 * suffix is appended to the key when retrieving the localized text from the
138 * application resources file.
139 * @param button buttons have a different default path to field.
140 * <code>true</code> if this field represents a button.
141 * @param mandatory if <code>true</code> an exception is thrown if no
142 * value is found for this combination.
143 * @return title, if this tag has one, otherwise <code>null</code>.
144 * @throws SystemException if the text cannot be retrieved because of a
145 * system failure, or if the field is mandatory and cannot be found.
146 */
147 public static String getDefaultLabel(
148 final Locale locale,
149 final String bundle,
150 final String fieldName,
151 final String labelKey,
152 final List labelArgs,
153 final String resourceFieldPath,
154 final String labelKeySuffix,
155 final boolean button,
156 boolean mandatory)
157 throws SystemException {
158 String labelKeySuffixAppend;
159 if (StringHandling.isNullOrEmpty(labelKeySuffix)) {
160 labelKeySuffixAppend = "";
161 } else {
162 labelKeySuffixAppend = "." + labelKeySuffix;
163 }
164 boolean reallyMandatory = mandatory;
165 if (guessLabel && mandatory) {
166 reallyMandatory = false;
167 }
168 String label = getDefaultMessage(locale, bundle, fieldName, labelKey,
169 labelArgs, labelKeySuffixAppend, resourceFieldPath, button,
170 reallyMandatory);
171 if (guessLabel
172 && StringHandling.isNullOrEmpty(label)
173 && !StringHandling.isNullOrEmpty(fieldName)) {
174 label = guessLabel(fieldName);
175 }
176 return label;
177 }
178 /***
179 * Guess what the label should be for a given field or class.
180 *
181 * @param fieldName name of the field or class to guess the label for
182 * @return guessed label.
183 */
184 private static String guessLabel(final String fieldName) {
185
186
187
188 StringBuffer guessedLabel = new StringBuffer();
189 int pos = 0;
190
191 guessedLabel.append((char)
192 Character.toUpperCase(fieldName.charAt(pos++)));
193 int len = fieldName.length();
194 boolean lastUpper = true;
195 while (pos < len) {
196 char ch = fieldName.charAt(pos);
197
198 if (Character.isUpperCase(ch)) {
199
200
201 if (!lastUpper) {
202 guessedLabel.append(" ");
203 }
204 lastUpper = true;
205 } else {
206 lastUpper = false;
207 }
208 guessedLabel.append(ch);
209 ++pos;
210 }
211 return guessedLabel.toString();
212 }
213 /***
214 * Work out the label field path, used to localize label strings.
215 *
216 * @param fieldName name of the field for which to return the title.
217 * @param resourceFieldPathString this is the prefix for all fields within
218 * the application resources.
219 * @param button buttons have a different default path to field.
220 * <code>true</code> if this field represents a button.
221 * @return label field path for this tag.
222 */
223 private static String getDefaultLabelFieldPath(
224 final String fieldName,
225 final String resourceFieldPathString,
226 final boolean button) {
227 StringBuffer labelFieldPath = new StringBuffer();
228 labelFieldPath.append(resourceFieldPathString);
229
230 if (!StringHandling.isNullOrEmpty(fieldName)) {
231 if (!labelFieldPath.toString().endsWith(".")
232 && (labelFieldPath.length() > 0)) {
233 labelFieldPath.append(".");
234 }
235 }
236 labelFieldPath.append(getDefaultPath(button, fieldName));
237 return labelFieldPath.toString();
238 }
239 /***
240 * Private helper to avoid repetition. This method is used in all of the
241 * <code>getDefault...</code> methods.
242 *
243 * @param locale Refer to {@link MessageResources#getMessage}.
244 * @param bundle Refer to {@link MessageResources#getMessage}.
245 * @param fieldName name of the field for which to return the message.
246 * @param key key used to access the message in the resources. If this
247 * is <code>null</code>, a default key is tried.
248 * @param args arguments used in conjunction with the key, in the
249 * message resources.
250 * @param suffix one of the <code>..._SUFFIX</code> constants from this
251 * class.
252 * @param resourceFieldPath this is the root of the message resource path
253 * searched. This is usually specific to an input mask, or a value object.
254 * @param button buttons have a different default path to field.
255 * <code>true</code> if this field represents a button.
256 * @param mandatory if <code>true</code> an exception is thrown if no
257 * value is found for this combination.
258 * @return text from the message resources.
259 * @throws SystemException if the text cannot be retrieved because of a
260 * system failure, or if the field is mandatory and cannot be found.
261 */
262 private static String getDefaultMessage(
263 final Locale locale,
264 final String bundle,
265 final String fieldName,
266 final String key,
267 final List args,
268 final String suffix,
269 final String resourceFieldPath,
270 final boolean button,
271 boolean mandatory)
272 throws SystemException {
273 List keysTried = new Vector();
274
275 String defaultPath = getDefaultPath(button, fieldName);
276 String actualKey = key;
277 String message = null;
278
279 if (actualKey != null) {
280 keysTried.add(actualKey);
281 message = getMessage(bundle,
282 locale, actualKey, args);
283 if (logger.isDebugEnabled()) {
284 logger.debug("Got "
285 + suffix
286 + "'"
287 + message
288 + "' from key '"
289 + actualKey
290 + "'");
291 }
292 }
293 if ((message == null) && (!StringHandling.isNullOrEmpty(fieldName))) {
294 actualKey = getDefaultLabelFieldPath(fieldName, resourceFieldPath,
295 button)
296 + suffix;
297 keysTried.add(actualKey);
298
299 message = getMessage(bundle,
300 locale, actualKey, args);
301 if (logger.isDebugEnabled()) {
302 logger.debug("Got "
303 + suffix
304 + " '"
305 + message
306 + "' from field name key '"
307 + actualKey
308 + "'");
309 }
310
311 if (message == null) {
312 actualKey = defaultPath
313 + suffix;
314 keysTried.add(actualKey);
315 message = getMessage(bundle,
316 locale,
317 actualKey,
318 args);
319 if (logger.isDebugEnabled()) {
320 logger.debug("Got "
321 + suffix
322 + " '"
323 + message
324 + "' from default path key '"
325 + actualKey
326 + "'");
327 }
328 }
329
330 if ((message == null)
331 && mandatory) {
332 throw new SystemException("No message resource "
333 + suffix
334 + " key found for field name '"
335 + fieldName
336 + "', bundle '"
337 + bundle
338 + "', resourceFieldPath '"
339 + resourceFieldPath
340 + suffix
341 + "'. Tried these keys: "
342 + keysTried
343 + ".");
344 }
345 }
346 return message;
347 }
348 /***
349 * The default message resource path depends on type - buttons have a
350 * different application resource path from other types
351 * @param button buttons have a different default path to field.
352 * <code>true</code> if this field represents a button.
353 * @param fieldName name of the field to default the path for.
354 * @return default path for this field.
355 */
356 private static String getDefaultPath(final boolean button,
357 final String fieldName) {
358 StringBuffer defaultPath;
359 if (button) {
360 defaultPath = new StringBuffer(
361 MessageResourcesHandling.SUBMIT_PREFIX);
362 } else {
363 defaultPath = new StringBuffer(
364 MessageResourcesHandling.FIELD_PREFIX);
365 }
366 defaultPath.append(fieldName);
367 return defaultPath.toString();
368 }
369 /***
370 * Get the default title for this field. If no title key is specifically
371 * set, the combination of
372 * <code>labelFieldPath + ".title"</code> is tried in the
373 * application resources. Failing that, the same is attempted using the
374 * default path (<code>defaultPath + ".title"</code>).
375 *
376 * @param locale Refer to {@link MessageResources#getMessage}.
377 * @param bundle Refer to {@link MessageResources#getMessage}.
378 * @param fieldName name of the field for which to return the title.
379 * @param titleKey key used to access the title in the resources. If this
380 * is <code>null</code>, a default key is tried.
381 * @param titleArgs arguments used in conjunction with the key, in the
382 * message resources.
383 * @param resourceFieldPath this is the root of the message resource path
384 * searched. This is usually specific to an input mask, or a value object.
385 * @param button buttons have a different default path to field.
386 * <code>true</code> if this field represents a button.
387 * @param mandatory if <code>true</code> an exception is thrown if no
388 * value is found for this combination.
389 * @return title, if this tag has one, otherwise <code>null</code>.
390 * @throws SystemException if the text cannot be retrieved because of a
391 * system failure, or if the field is mandatory and cannot be found.
392 */
393 public static String getDefaultTitle(
394 final Locale locale,
395 final String bundle,
396 final String fieldName,
397 final String titleKey,
398 final List titleArgs,
399 final String resourceFieldPath,
400 final boolean button,
401 boolean mandatory)
402 throws SystemException {
403 return getDefaultMessage(locale, bundle, fieldName, titleKey, titleArgs,
404 TITLE_SUFFIX, resourceFieldPath, button, mandatory);
405 }
406 /***
407 * Get the default value for this field. If no value key is specifically
408 * set, the combination of
409 * <code>labelFieldPath + ".value"</code> is tried in the
410 * application resources. Failing that, the same is attempted using the
411 * default path (<code>defaultPath + ".value"</code>).
412 *
413 * @param locale Refer to {@link MessageResources#getMessage}.
414 * @param bundle Refer to {@link MessageResources#getMessage}.
415 * @param fieldName name of the field for which to return the title.
416 * @param valueKey key used to access the title in the resources. If this
417 * is <code>null</code>, a default key is tried.
418 * @param valueArgs arguments used in conjunction with the key, in the
419 * message resources.
420 * @param resourceFieldPath this is the root of the message resource path
421 * searched. This is usually specific to an input mask, or a value object.
422 * @param button buttons have a different default path to field.
423 * <code>true</code> if this field represents a button.
424 * @param mandatory if <code>true</code> an exception is thrown if no
425 * value is found for this combination.
426 * @return title, if this tag has one, otherwise <code>null</code>.
427 * @throws SystemException if the text cannot be retrieved because of a
428 * system failure, or if the field is mandatory and cannot be found.
429 */
430 public static String getDefaultValue(
431 final Locale locale,
432 final String bundle,
433 final String fieldName,
434 final String valueKey,
435 final List valueArgs,
436 final String resourceFieldPath,
437 final boolean button,
438 boolean mandatory)
439 throws SystemException {
440 return getDefaultMessage(locale, bundle, fieldName, valueKey, valueArgs,
441 VALUE_SUFFIX, resourceFieldPath, button, mandatory);
442 }
443
444 /***
445 * This first tries using the
446 * bundle you provided. If that returns <code>null</code>, it tries again
447 * without a bundle.
448 * @param bundle Refer to {@link MessageResources#getMessage}.
449 * @param locale Refer to {@link MessageResources#getMessage}.
450 * @param key Refer to {@link MessageResources#getMessage}.
451 * @param argsList Will be converted to an array of objects. Refer to
452 * {@link MessageResources#getMessage}.
453 *
454 * @return Refer to {@link MessageResources#getMessage}.
455 * @throws SystemException Refer to {@link MessageResources#getMessage}.
456 * @see TagUtils#getInstance
457 */
458 public static String getMessage(final String bundle,
459 final Locale locale, final String key, final List argsList)
460 throws SystemException {
461 Object [] args;
462 if (argsList == null) {
463 args = new Object[] {
464 };
465 } else {
466 args = argsList.toArray();
467 }
468 return getMessage(bundle, locale, key, args);
469 }
470
471 /***
472 * This first tries using the
473 * bundle you provided. If that returns <code>null</code>, it tries again
474 * without a bundle.
475 * @param bundle Refer to {@link MessageResources#getMessage}.
476 * @param locale Refer to {@link MessageResources#getMessage}.
477 * @param key Refer to {@link MessageResources#getMessage}.
478 * @param args Refer to {@link MessageResources#getMessage}.
479 *
480 * @return Refer to {@link MessageResources#getMessage}.
481 * @throws SystemException Refer to {@link MessageResources#getMessage}.
482 * @see TagUtils#getInstance
483 */
484 public static String getMessage(final String bundle,
485 final Locale locale, final String key, final Object[] args)
486 throws SystemException {
487 assert (key != null);
488 String message = null;
489 if (bundle != null) {
490 MessageResources messages = getMessages(bundle);
491 message = messages.getMessage(locale, key, args);
492 }
493 if (message == null) {
494 MessageResources messages = getMessages(null);
495 message = messages.getMessage(locale, key, args);
496 }
497 return message;
498 }
499 /***
500 * This first tries using the
501 * bundle you provided. If that returns <code>null</code>, it tries again
502 * without a bundle.
503 * @param bundle Refer to {@link MessageResources#getMessage}.
504 * @param locale Refer to {@link MessageResources#getMessage}.
505 * @param key Refer to {@link MessageResources#getMessage}.
506 * @param args Will be converted to an array of objects. Refer to
507 * {@link MessageResources#getMessage}.
508 *
509 * @return Refer to {@link MessageResources#getMessage}.
510 * @throws SystemException Refer to {@link MessageResources#getMessage}.
511 * @see TagUtils#getInstance
512 */
513 public static String getMessage(final String bundle,
514 final String locale, final String key, final List args)
515 throws SystemException {
516 return getMessage(bundle, new Locale(locale), key, args.toArray());
517 }
518 /***
519 * This first tries using the
520 * bundle you provided. If that returns <code>null</code>, it tries again
521 * without a bundle.
522 * @param bundle Refer to {@link MessageResources#getMessage}.
523 * @param locale Refer to {@link MessageResources#getMessage}.
524 * @param key Refer to {@link MessageResources#getMessage}.
525 * @param args Refer to {@link MessageResources#getMessage}.
526 *
527 * @return Refer to {@link MessageResources#getMessage}.
528 * @throws SystemException Refer to {@link MessageResources#getMessage}.
529 * @see TagUtils#getInstance
530 */
531 public static String getMessage(final String bundle,
532 final String locale, final String key, final Object[] args)
533 throws SystemException {
534 return getMessage(bundle, new Locale(locale), key, args);
535 }
536
537 /***
538 * Get a bundle with the given name.
539 * @param bundle message resources identifier - see <code>Struts</code>
540 * docu.
541 * @return resources message resources instance.
542 * @throws SystemException if the message resources are undefined.
543 */
544 public static MessageResources getMessages(String bundle)
545 throws SystemException {
546 if (bundle == null) {
547 bundle = NULL_BUNDLE;
548 }
549 MessageResources messageResources = (MessageResources )
550 messageResourcesMap.get(bundle);
551 if (messageResources == null) {
552 throw new SystemException("Resource bundle '"
553 + bundle
554 + "' is undefined.");
555 }
556 return messageResources;
557 }
558 /***
559 * @return Returns the guessLabel.
560 */
561 public static boolean isGuessLabel() {
562 return guessLabel;
563 }
564
565 /***
566 * Register resources for a given bundle.
567 * @param bundle message resources identifier - see <code>Struts</code>
568 * docu.
569 * @param fullPath full path to the resources
570 */
571 public static synchronized void registerMessages(String bundle,
572 String fullPath) {
573 assert (fullPath != null);
574
575 if (bundle == null) {
576 bundle = NULL_BUNDLE;
577 }
578 MessageResources messageResources =
579 MessageResources.getMessageResources(fullPath);
580 assert (messageResources != null);
581 messageResourcesMap.put(bundle, messageResources);
582 }
583 /***
584 * Refer to {@link #getguessLabel}.
585 * @param guessLabelParam Refer to {@link #getguessLabel}.
586 */
587 public static synchronized void setGuessLabel(boolean guessLabelParam) {
588 if (logger.isDebugEnabled()) {
589 logger.debug("Setting guessLabel. Before '" + guessLabel
590 + "', after '" + guessLabelParam + "'");
591 }
592 guessLabel = guessLabelParam;
593 }
594
595 /***
596 * Private default constructor enforces utility class handling.
597 */
598 private MessageResourcesHandling() {
599 }
600 }