1 /***
2 * Licensed under the Artistic License; you may not use this file
3 * except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://displaytag.sourceforge.net/license.html
7 *
8 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12 package org.displaytag.properties;
13
14 import java.io.IOException;
15 import java.util.Enumeration;
16 import java.util.HashMap;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.MissingResourceException;
20 import java.util.Properties;
21 import java.util.ResourceBundle;
22
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.jsp.PageContext;
25 import javax.servlet.jsp.tagext.Tag;
26
27 import org.apache.commons.lang.ClassUtils;
28 import org.apache.commons.lang.StringUtils;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.displaytag.Messages;
32 import org.displaytag.exception.FactoryInstantiationException;
33 import org.displaytag.exception.TablePropertiesLoadException;
34 import org.displaytag.exception.WrappedRuntimeException;
35 import org.displaytag.localization.I18nResourceProvider;
36 import org.displaytag.localization.LocaleResolver;
37 import org.displaytag.util.DefaultRequestHelperFactory;
38 import org.displaytag.util.ReflectHelper;
39 import org.displaytag.util.RequestHelperFactory;
40
41
42 /***
43 * The properties used by the Table tags. The properties are loaded in the following order, in increasing order of
44 * priority. The locale of getInstance() is used to determine the locale of the property file to use; if the key
45 * required does not exist in the specified file, the key will be loaded from a more general property file.
46 * <ol>
47 * <li>First, from the TableTag.properties included with the DisplayTag distribution.</li>
48 * <li>Then, from the file displaytag.properties, if it is present; these properties are intended to be set by the user
49 * for sitewide application. Messages are gathered according to the Locale of the property file.</li>
50 * <li>Finally, if this class has a userProperties defined, all of the properties from that Properties object are
51 * copied in as well.</li>
52 * </ol>
53 * @author Fabrizio Giustina
54 * @author rapruitt
55 * @version $Revision: 1.35 $ ($Author: fgiust $)
56 */
57 public final class TableProperties implements Cloneable
58 {
59
60 /***
61 * name of the default properties file name ("TableTag.properties").
62 */
63 public static final String DEFAULT_FILENAME = "TableTag.properties";
64
65 /***
66 * The name of the local properties file that is searched for on the classpath. Settings in this file will override
67 * the defaults loaded from TableTag.properties.
68 */
69 public static final String LOCAL_PROPERTIES = "displaytag";
70
71 /***
72 * property <code>export.banner</code>.
73 */
74 public static final String PROPERTY_STRING_EXPORTBANNER = "export.banner";
75
76 /***
77 * property <code>export.banner.sepchar</code>.
78 */
79 public static final String PROPERTY_STRING_EXPORTBANNER_SEPARATOR = "export.banner.sepchar";
80
81 /***
82 * property <code>export.decorated</code>.
83 */
84 public static final String PROPERTY_BOOLEAN_EXPORTDECORATED = "export.decorated";
85
86 /***
87 * property <code>export.amount</code>.
88 */
89 public static final String PROPERTY_STRING_EXPORTAMOUNT = "export.amount";
90
91 /***
92 * property <code>sort.amount</code>.
93 */
94 public static final String PROPERTY_STRING_SORTAMOUNT = "sort.amount";
95
96 /***
97 * property <code>basic.show.header</code>.
98 */
99 public static final String PROPERTY_BOOLEAN_SHOWHEADER = "basic.show.header";
100
101 /***
102 * property <code>basic.msg.empty_list</code>.
103 */
104 public static final String PROPERTY_STRING_EMPTYLIST_MESSAGE = "basic.msg.empty_list";
105
106 /***
107 * property <code>basic.msg.empty_list_row</code>.
108 */
109 public static final String PROPERTY_STRING_EMPTYLISTROW_MESSAGE = "basic.msg.empty_list_row";
110
111 /***
112 * property <code>basic.empty.showtable</code>.
113 */
114 public static final String PROPERTY_BOOLEAN_EMPTYLIST_SHOWTABLE = "basic.empty.showtable";
115
116 /***
117 * property <code>paging.banner.placement</code>.
118 */
119 public static final String PROPERTY_STRING_BANNER_PLACEMENT = "paging.banner.placement";
120
121 /***
122 * property <code>error.msg.invalid_page</code>.
123 */
124 public static final String PROPERTY_STRING_PAGING_INVALIDPAGE = "error.msg.invalid_page";
125
126 /***
127 * property <code>paging.banner.item_name</code>.
128 */
129 public static final String PROPERTY_STRING_PAGING_ITEM_NAME = "paging.banner.item_name";
130
131 /***
132 * property <code>paging.banner.items_name</code>.
133 */
134 public static final String PROPERTY_STRING_PAGING_ITEMS_NAME = "paging.banner.items_name";
135
136 /***
137 * property <code>paging.banner.no_items_found</code>.
138 */
139 public static final String PROPERTY_STRING_PAGING_NOITEMS = "paging.banner.no_items_found";
140
141 /***
142 * property <code>paging.banner.one_item_found</code>.
143 */
144 public static final String PROPERTY_STRING_PAGING_FOUND_ONEITEM = "paging.banner.one_item_found";
145
146 /***
147 * property <code>paging.banner.all_items_found</code>.
148 */
149 public static final String PROPERTY_STRING_PAGING_FOUND_ALLITEMS = "paging.banner.all_items_found";
150
151 /***
152 * property <code>paging.banner.some_items_found</code>.
153 */
154 public static final String PROPERTY_STRING_PAGING_FOUND_SOMEITEMS = "paging.banner.some_items_found";
155
156 /***
157 * property <code>paging.banner.group_size</code>.
158 */
159 public static final String PROPERTY_INT_PAGING_GROUPSIZE = "paging.banner.group_size";
160
161 /***
162 * property <code>paging.banner.onepage</code>.
163 */
164 public static final String PROPERTY_STRING_PAGING_BANNER_ONEPAGE = "paging.banner.onepage";
165
166 /***
167 * property <code>paging.banner.first</code>.
168 */
169 public static final String PROPERTY_STRING_PAGING_BANNER_FIRST = "paging.banner.first";
170
171 /***
172 * property <code>paging.banner.last</code>.
173 */
174 public static final String PROPERTY_STRING_PAGING_BANNER_LAST = "paging.banner.last";
175
176 /***
177 * property <code>paging.banner.full</code>.
178 */
179 public static final String PROPERTY_STRING_PAGING_BANNER_FULL = "paging.banner.full";
180
181 /***
182 * property <code>paging.banner.page.link</code>.
183 */
184 public static final String PROPERTY_STRING_PAGING_PAGE_LINK = "paging.banner.page.link";
185
186 /***
187 * property <code>paging.banner.page.selected</code>.
188 */
189 public static final String PROPERTY_STRING_PAGING_PAGE_SELECTED = "paging.banner.page.selected";
190
191 /***
192 * property <code>paging.banner.page.separator</code>.
193 */
194 public static final String PROPERTY_STRING_PAGING_PAGE_SPARATOR = "paging.banner.page.separator";
195
196 /***
197 * property <code>factory.requestHelper</code>.
198 */
199 public static final String PROPERTY_CLASS_REQUESTHELPERFACTORY = "factory.requestHelper";
200
201 /***
202 * property <code>locale.provider</code>.
203 */
204 public static final String PROPERTY_CLASS_LOCALEPROVIDER = "locale.provider";
205
206 /***
207 * property <code>locale.resolver</code>.
208 */
209 public static final String PROPERTY_CLASS_LOCALERESOLVER = "locale.resolver";
210
211 /***
212 * property <code>css.tr.even</code>: holds the name of the css class for even rows. Defaults to
213 * <code>even</code>.
214 */
215 public static final String PROPERTY_CSS_TR_EVEN = "css.tr.even";
216
217 /***
218 * property <code>css.tr.odd</code>: holds the name of the css class for odd rows. Defaults to <code>odd</code>.
219 */
220 public static final String PROPERTY_CSS_TR_ODD = "css.tr.odd";
221
222 /***
223 * property <code>css.table</code>: holds the name of the css class added to the main table tag. By default no
224 * css class is added.
225 */
226 public static final String PROPERTY_CSS_TABLE = "css.table";
227
228 /***
229 * property <code>css.th.sortable</code>: holds the name of the css class added to the the header of a sortable
230 * column. By default no css class is added.
231 */
232 public static final String PROPERTY_CSS_TH_SORTABLE = "css.th.sortable";
233
234 /***
235 * property <code>css.th.sorted</code>: holds the name of the css class added to the the header of a sorted
236 * column. Defaults to <code>sorted</code>.
237 */
238 public static final String PROPERTY_CSS_TH_SORTED = "css.th.sorted";
239
240 /***
241 * property <code>css.th.ascending</code>: holds the name of the css class added to the the header of a column
242 * sorted in ascending order. Defaults to <code>order1</code>.
243 */
244 public static final String PROPERTY_CSS_TH_SORTED_ASCENDING = "css.th.ascending";
245
246 /***
247 * property <code>css.th.descending</code>: holds the name of the css class added to the the header of a column
248 * sorted in descending order. Defaults to <code>order2</code>.
249 */
250 public static final String PROPERTY_CSS_TH_SORTED_DESCENDING = "css.th.descending";
251
252 /***
253 * prefix used for all the properties related to export ("export"). The full property name is <code>export.</code>
254 * <em>[export type]</em><code>.</code><em>[property name]</em>
255 */
256 public static final String PROPERTY_EXPORT_PREFIX = "export";
257
258 /***
259 * property <code>export.types</code>: holds the list of export available export types.
260 */
261 public static final String PROPERTY_EXPORTTYPES = "export.types";
262
263 /***
264 * export property <code>label</code>.
265 */
266 public static final String EXPORTPROPERTY_STRING_LABEL = "label";
267
268 /***
269 * export property <code>class</code>.
270 */
271 public static final String EXPORTPROPERTY_STRING_CLASS = "class";
272
273 /***
274 * export property <code>include_header</code>.
275 */
276 public static final String EXPORTPROPERTY_BOOLEAN_EXPORTHEADER = "include_header";
277
278 /***
279 * export property <code>filename</code>.
280 */
281 public static final String EXPORTPROPERTY_STRING_FILENAME = "filename";
282
283 /***
284 * Separator char used in property names.
285 */
286 private static final char SEP = '.';
287
288 /***
289 * logger.
290 */
291 private static Log log = LogFactory.getLog(TableProperties.class);
292
293 /***
294 * The userProperties are local, non-default properties; these settings override the defaults from
295 * displaytag.properties and TableTag.properties.
296 */
297 private static Properties userProperties = new Properties();
298
299 /***
300 * Configured resource provider. If no ResourceProvider is configured, an no-op one is used. This instance is
301 * initialized at first use and shared.
302 */
303 private static I18nResourceProvider resourceProvider;
304
305 /***
306 * Configured locale resolver.
307 */
308 private static LocaleResolver localeResolver;
309
310 /***
311 * TableProperties for each locale are loaded as needed, and cloned for public usage.
312 */
313 private static Map prototypes = new HashMap();
314
315 /***
316 * Loaded properties (defaults from defaultProperties + custom from bundle).
317 */
318 private Properties properties;
319
320 /***
321 * The locale for these properties.
322 */
323 private Locale locale;
324
325 /***
326 * Setter for I18nResourceProvider. A resource provider is usually set using displaytag properties, this accessor is
327 * needed for tests.
328 * @param provider I18nResourceProvider instance
329 */
330 protected static void setResourceProvider(I18nResourceProvider provider)
331 {
332 resourceProvider = provider;
333 }
334
335 /***
336 * Setter for LocaleResolver. A locale resolver is usually set using displaytag properties, this accessor is needed
337 * for tests.
338 * @param resolver LocaleResolver instance
339 */
340 protected static void setLocaleResolver(LocaleResolver resolver)
341 {
342 localeResolver = resolver;
343 }
344
345 /***
346 * Loads default properties (TableTag.properties).
347 * @return loaded properties
348 * @throws TablePropertiesLoadException if default properties file can't be found
349 */
350 private static Properties loadBuiltInProperties() throws TablePropertiesLoadException
351 {
352 Properties defaultProperties = new Properties();
353
354 try
355 {
356 defaultProperties.load(TableProperties.class.getResourceAsStream(DEFAULT_FILENAME));
357 }
358 catch (IOException e)
359 {
360 throw new TablePropertiesLoadException(TableProperties.class, DEFAULT_FILENAME, e);
361 }
362
363 return defaultProperties;
364 }
365
366 /***
367 * Loads user properties (displaytag.properties) according to the given locale. User properties are not guarantee to
368 * exist, so the method can return <code>null</code> (no exception will be thrown).
369 * @param locale requested Locale
370 * @return loaded properties
371 */
372 private static ResourceBundle loadUserProperties(Locale locale)
373 {
374 ResourceBundle bundle = null;
375 try
376 {
377 bundle = ResourceBundle.getBundle(LOCAL_PROPERTIES, locale);
378 }
379 catch (MissingResourceException e)
380 {
381
382 try
383 {
384 bundle = ResourceBundle.getBundle(LOCAL_PROPERTIES, locale, Thread
385 .currentThread()
386 .getContextClassLoader());
387 }
388 catch (MissingResourceException mre)
389 {
390 if (log.isDebugEnabled())
391 {
392 log.debug(Messages.getString("TableProperties.propertiesnotfound",
393 new Object[]{mre.getMessage()}));
394 }
395 }
396 }
397
398 return bundle;
399 }
400
401 /***
402 * Returns the configured Locale Resolver. This method is called before the loading of localized properties.
403 * @return LocaleResolver instance.
404 * @throws TablePropertiesLoadException if the default <code>TableTag.properties</code> file is not found.
405 */
406 public static LocaleResolver getLocaleResolverInstance() throws TablePropertiesLoadException
407 {
408
409 String className = null;
410
411 ResourceBundle defaultUserProperties = loadUserProperties(Locale.getDefault());
412
413
414 if (defaultUserProperties != null)
415 {
416 try
417 {
418 className = defaultUserProperties.getString(PROPERTY_CLASS_LOCALERESOLVER);
419 }
420 catch (MissingResourceException e)
421 {
422
423 }
424 }
425
426
427 if (className == null)
428 {
429 Properties defaults = loadBuiltInProperties();
430 className = defaults.getProperty(PROPERTY_CLASS_LOCALERESOLVER);
431 }
432
433 if (localeResolver == null)
434 {
435 if (className != null)
436 {
437 try
438 {
439 Class classProperty = ReflectHelper.classForName(className);
440 localeResolver = (LocaleResolver) classProperty.newInstance();
441
442 log.info(Messages.getString("TableProperties.classinitializedto",
443 new Object[]{ClassUtils.getShortClassName(LocaleResolver.class), className}));
444 }
445 catch (Throwable e)
446 {
447 log.warn(Messages.getString("TableProperties.errorloading",
448 new Object[]{
449 ClassUtils.getShortClassName(LocaleResolver.class),
450 e.getClass().getName(),
451 e.getMessage()}));
452 }
453 }
454 else
455 {
456 log.info(Messages.getString("TableProperties.noconfigured",
457 new Object[]{ClassUtils.getShortClassName(LocaleResolver.class)}));
458 }
459
460
461 if (localeResolver == null)
462 {
463
464 localeResolver = new LocaleResolver()
465 {
466
467 public Locale resolveLocale(HttpServletRequest request)
468 {
469 return request.getLocale();
470 }
471 };
472 }
473 }
474
475 return localeResolver;
476 }
477
478 /***
479 * Initialize a new TableProperties loading the default properties file and the user defined one. There is no
480 * caching used here, caching is assumed to occur in the getInstance factory method.
481 * @param myLocale the locale we are in
482 * @throws TablePropertiesLoadException for errors during loading of properties files
483 */
484 private TableProperties(Locale myLocale) throws TablePropertiesLoadException
485 {
486 this.locale = myLocale;
487
488 Properties defaultProperties = loadBuiltInProperties();
489
490 properties = new Properties(defaultProperties);
491 addProperties(myLocale);
492
493
494
495 Enumeration keys = userProperties.keys();
496 while (keys.hasMoreElements())
497 {
498 String key = (String) keys.nextElement();
499 if (key != null)
500 {
501 properties.setProperty(key, (String) userProperties.get(key));
502 }
503 }
504 }
505
506 /***
507 * Try to load the properties from the local properties file, displaytag.properties, and merge them into the
508 * existing properties.
509 * @param userLocale the locale from which the properties are to be loaded
510 */
511 private void addProperties(Locale userLocale)
512 {
513 ResourceBundle bundle = loadUserProperties(userLocale);
514
515 if (bundle != null)
516 {
517 Enumeration keys = bundle.getKeys();
518 while (keys.hasMoreElements())
519 {
520 String key = (String) keys.nextElement();
521 properties.setProperty(key, bundle.getString(key));
522 }
523 }
524 }
525
526 /***
527 * Clones the properties as well.
528 * @return a new clone of oneself
529 */
530 protected Object clone()
531 {
532 TableProperties twin;
533 try
534 {
535 twin = (TableProperties) super.clone();
536 }
537 catch (CloneNotSupportedException e)
538 {
539
540 throw new WrappedRuntimeException(getClass(), e);
541 }
542 twin.properties = (Properties) this.properties.clone();
543 return twin;
544 }
545
546 /***
547 * Returns a new TableProperties instance for the given locale.
548 * @param request HttpServletRequest needed to extract the locale to use. If null the default locale will be used.
549 * @return TableProperties instance
550 */
551 public static TableProperties getInstance(HttpServletRequest request)
552 {
553 Locale locale;
554 if (request != null)
555 {
556 locale = getLocaleResolverInstance().resolveLocale(request);
557 }
558 else
559 {
560
561 locale = Locale.getDefault();
562 }
563
564 TableProperties props = (TableProperties) prototypes.get(locale);
565 if (props == null)
566 {
567 TableProperties lprops = new TableProperties(locale);
568 prototypes.put(locale, lprops);
569 props = lprops;
570 }
571 return (TableProperties) props.clone();
572 }
573
574 /***
575 * Unload all cached properties. This will not clear properties set by by setUserProperties; you must clear those
576 * manually.
577 */
578 public static void clearProperties()
579 {
580 prototypes.clear();
581 }
582
583 /***
584 * Local, non-default properties; these settings override the defaults from displaytag.properties and
585 * TableTag.properties. Please note that the values are copied in, so that multiple calls with non-overlapping
586 * properties will be merged, not overwritten. Note: setUserProperties() MUST BE CALLED before the first
587 * TableProperties instantation.
588 * @param overrideProperties - The local, non-default properties
589 */
590 public static void setUserProperties(Properties overrideProperties)
591 {
592
593
594 Enumeration keys = overrideProperties.keys();
595 while (keys.hasMoreElements())
596 {
597 String key = (String) keys.nextElement();
598 if (key != null)
599 {
600 userProperties.setProperty(key, (String) overrideProperties.get(key));
601 }
602 }
603 }
604
605 /***
606 * The locale for which these properties are intended.
607 * @return the locale
608 */
609 public Locale getLocale()
610 {
611 return locale;
612 }
613
614 /***
615 * Getter for the <code>PROPERTY_STRING_PAGING_INVALIDPAGE</code> property.
616 * @return String
617 */
618 public String getPagingInvalidPage()
619 {
620 return getProperty(PROPERTY_STRING_PAGING_INVALIDPAGE);
621 }
622
623 /***
624 * Getter for the <code>PROPERTY_STRING_PAGING_ITEM_NAME</code> property.
625 * @return String
626 */
627 public String getPagingItemName()
628 {
629 return getProperty(PROPERTY_STRING_PAGING_ITEM_NAME);
630 }
631
632 /***
633 * Getter for the <code>PROPERTY_STRING_PAGING_ITEMS_NAME</code> property.
634 * @return String
635 */
636 public String getPagingItemsName()
637 {
638 return getProperty(PROPERTY_STRING_PAGING_ITEMS_NAME);
639 }
640
641 /***
642 * Getter for the <code>PROPERTY_STRING_PAGING_NOITEMS</code> property.
643 * @return String
644 */
645 public String getPagingFoundNoItems()
646 {
647 return getProperty(PROPERTY_STRING_PAGING_NOITEMS);
648 }
649
650 /***
651 * Getter for the <code>PROPERTY_STRING_PAGING_FOUND_ONEITEM</code> property.
652 * @return String
653 */
654 public String getPagingFoundOneItem()
655 {
656 return getProperty(PROPERTY_STRING_PAGING_FOUND_ONEITEM);
657 }
658
659 /***
660 * Getter for the <code>PROPERTY_STRING_PAGING_FOUND_ALLITEMS</code> property.
661 * @return String
662 */
663 public String getPagingFoundAllItems()
664 {
665 return getProperty(PROPERTY_STRING_PAGING_FOUND_ALLITEMS);
666 }
667
668 /***
669 * Getter for the <code>PROPERTY_STRING_PAGING_FOUND_SOMEITEMS</code> property.
670 * @return String
671 */
672 public String getPagingFoundSomeItems()
673 {
674 return getProperty(PROPERTY_STRING_PAGING_FOUND_SOMEITEMS);
675 }
676
677 /***
678 * Getter for the <code>PROPERTY_INT_PAGING_GROUPSIZE</code> property.
679 * @return int
680 */
681 public int getPagingGroupSize()
682 {
683
684 return getIntProperty(PROPERTY_INT_PAGING_GROUPSIZE, 8);
685 }
686
687 /***
688 * Getter for the <code>PROPERTY_STRING_PAGING_BANNER_ONEPAGE</code> property.
689 * @return String
690 */
691 public String getPagingBannerOnePage()
692 {
693 return getProperty(PROPERTY_STRING_PAGING_BANNER_ONEPAGE);
694 }
695
696 /***
697 * Getter for the <code>PROPERTY_STRING_PAGING_BANNER_FIRST</code> property.
698 * @return String
699 */
700 public String getPagingBannerFirst()
701 {
702 return getProperty(PROPERTY_STRING_PAGING_BANNER_FIRST);
703 }
704
705 /***
706 * Getter for the <code>PROPERTY_STRING_PAGING_BANNER_LAST</code> property.
707 * @return String
708 */
709 public String getPagingBannerLast()
710 {
711 return getProperty(PROPERTY_STRING_PAGING_BANNER_LAST);
712 }
713
714 /***
715 * Getter for the <code>PROPERTY_STRING_PAGING_BANNER_FULL</code> property.
716 * @return String
717 */
718 public String getPagingBannerFull()
719 {
720 return getProperty(PROPERTY_STRING_PAGING_BANNER_FULL);
721 }
722
723 /***
724 * Getter for the <code>PROPERTY_STRING_PAGING_PAGE_LINK</code> property.
725 * @return String
726 */
727 public String getPagingPageLink()
728 {
729 return getProperty(PROPERTY_STRING_PAGING_PAGE_LINK);
730 }
731
732 /***
733 * Getter for the <code>PROPERTY_STRING_PAGING_PAGE_SELECTED</code> property.
734 * @return String
735 */
736 public String getPagingPageSelected()
737 {
738 return getProperty(PROPERTY_STRING_PAGING_PAGE_SELECTED);
739 }
740
741 /***
742 * Getter for the <code>PROPERTY_STRING_PAGING_PAGE_SPARATOR</code> property.
743 * @return String
744 */
745 public String getPagingPageSeparator()
746 {
747 return getProperty(PROPERTY_STRING_PAGING_PAGE_SPARATOR);
748 }
749
750 /***
751 * Is the given export option enabled?
752 * @param exportType instance of MediaTypeEnum
753 * @return boolean true if export is enabled
754 */
755 public boolean getAddExport(MediaTypeEnum exportType)
756 {
757 return getBooleanProperty(PROPERTY_EXPORT_PREFIX + SEP + exportType.getName());
758 }
759
760 /***
761 * Should headers be included in given export type?
762 * @param exportType instance of MediaTypeEnum
763 * @return boolean true if export should include headers
764 */
765 public boolean getExportHeader(MediaTypeEnum exportType)
766 {
767 return getBooleanProperty(PROPERTY_EXPORT_PREFIX
768 + SEP
769 + exportType.getName()
770 + SEP
771 + EXPORTPROPERTY_BOOLEAN_EXPORTHEADER);
772 }
773
774 /***
775 * Returns the label for the given export option.
776 * @param exportType instance of MediaTypeEnum
777 * @return String label
778 */
779 public String getExportLabel(MediaTypeEnum exportType)
780 {
781 return getProperty(PROPERTY_EXPORT_PREFIX + SEP + exportType.getName() + SEP + EXPORTPROPERTY_STRING_LABEL);
782 }
783
784 /***
785 * Returns the file name for the given media. Can be null
786 * @param exportType instance of MediaTypeEnum
787 * @return String filename
788 */
789 public String getExportFileName(MediaTypeEnum exportType)
790 {
791 return getProperty(PROPERTY_EXPORT_PREFIX + SEP + exportType.getName() + SEP + EXPORTPROPERTY_STRING_FILENAME);
792 }
793
794 /***
795 * Getter for the <code>PROPERTY_BOOLEAN_EXPORTDECORATED</code> property.
796 * @return boolean <code>true</code> if decorators should be used in exporting
797 */
798 public boolean getExportDecorated()
799 {
800 return getBooleanProperty(PROPERTY_BOOLEAN_EXPORTDECORATED);
801 }
802
803 /***
804 * Getter for the <code>PROPERTY_STRING_EXPORTBANNER</code> property.
805 * @return String
806 */
807 public String getExportBanner()
808 {
809 return getProperty(PROPERTY_STRING_EXPORTBANNER);
810 }
811
812 /***
813 * Getter for the <code>PROPERTY_STRING_EXPORTBANNER_SEPARATOR</code> property.
814 * @return String
815 */
816 public String getExportBannerSeparator()
817 {
818 return getProperty(PROPERTY_STRING_EXPORTBANNER_SEPARATOR);
819 }
820
821 /***
822 * Getter for the <code>PROPERTY_BOOLEAN_SHOWHEADER</code> property.
823 * @return boolean
824 */
825 public boolean getShowHeader()
826 {
827 return getBooleanProperty(PROPERTY_BOOLEAN_SHOWHEADER);
828 }
829
830 /***
831 * Getter for the <code>PROPERTY_STRING_EMPTYLIST_MESSAGE</code> property.
832 * @return String
833 */
834 public String getEmptyListMessage()
835 {
836 return getProperty(PROPERTY_STRING_EMPTYLIST_MESSAGE);
837 }
838
839 /***
840 * Getter for the <code>PROPERTY_STRING_EMPTYLISTROW_MESSAGE</code> property.
841 * @return String
842 */
843 public String getEmptyListRowMessage()
844 {
845 return getProperty(PROPERTY_STRING_EMPTYLISTROW_MESSAGE);
846 }
847
848 /***
849 * Getter for the <code>PROPERTY_BOOLEAN_EMPTYLIST_SHOWTABLE</code> property.
850 * @return boolean <code>true</code> if table should be displayed also if no items are found
851 */
852 public boolean getEmptyListShowTable()
853 {
854 return getBooleanProperty(PROPERTY_BOOLEAN_EMPTYLIST_SHOWTABLE);
855 }
856
857 /***
858 * Getter for the <code>PROPERTY_STRING_EXPORTAMOUNT</code> property.
859 * @return boolean <code>true</code> if <code>export.amount</code> is <code>list</code>
860 */
861 public boolean getExportFullList()
862 {
863 return "list".equals(getProperty(PROPERTY_STRING_EXPORTAMOUNT));
864 }
865
866 /***
867 * Getter for the <code>PROPERTY_STRING_SORTAMOUNT</code> property.
868 * @return boolean <code>true</code> if <code>sort.amount</code> is <code>list</code>
869 */
870 public boolean getSortFullList()
871 {
872 return "list".equals(getProperty(PROPERTY_STRING_SORTAMOUNT));
873 }
874
875 /***
876 * Should paging banner be added before the table?
877 * @return boolean
878 */
879 public boolean getAddPagingBannerTop()
880 {
881 String placement = getProperty(PROPERTY_STRING_BANNER_PLACEMENT);
882 return "top".equals(placement) || "both".equals(placement);
883 }
884
885 /***
886 * Should paging banner be added after the table?
887 * @return boolean
888 */
889 public boolean getAddPagingBannerBottom()
890 {
891 String placement = getProperty(PROPERTY_STRING_BANNER_PLACEMENT);
892 return "bottom".equals(placement) || "both".equals(placement);
893 }
894
895 /***
896 * Returns the appropriate css class for a table row.
897 * @param rowNumber row number
898 * @return the value of <code>PROPERTY_CSS_TR_EVEN</code> if rowNumber is even or <code>PROPERTY_CSS_TR_ODD</code>
899 * if rowNumber is odd.
900 */
901 public String getCssRow(int rowNumber)
902 {
903 return getProperty((rowNumber % 2 == 0) ? PROPERTY_CSS_TR_ODD : PROPERTY_CSS_TR_EVEN);
904 }
905
906 /***
907 * Returns the appropriate css class for a sorted column header.
908 * @param ascending <code>true</code> if column is sorded in ascending order.
909 * @return the value of <code>PROPERTY_CSS_TH_SORTED_ASCENDING</code> if column is sorded in ascending order or
910 * <code>PROPERTY_CSS_TH_SORTED_DESCENDING</code> if column is sorded in descending order.
911 */
912 public String getCssOrder(boolean ascending)
913 {
914 return getProperty(ascending ? PROPERTY_CSS_TH_SORTED_ASCENDING : PROPERTY_CSS_TH_SORTED_DESCENDING);
915 }
916
917 /***
918 * Returns the configured css class for a sorted column header.
919 * @return the value of <code>PROPERTY_CSS_TH_SORTED</code>
920 */
921 public String getCssSorted()
922 {
923 return getProperty(PROPERTY_CSS_TH_SORTED);
924 }
925
926 /***
927 * Returns the configured css class for the main table tag.
928 * @return the value of <code>PROPERTY_CSS_TABLE</code>
929 */
930 public String getCssTable()
931 {
932 return getProperty(PROPERTY_CSS_TABLE);
933 }
934
935 /***
936 * Returns the configured css class for a sortable column header.
937 * @return the value of <code>PROPERTY_CSS_TH_SORTABLE</code>
938 */
939 public String getCssSortable()
940 {
941 return getProperty(PROPERTY_CSS_TH_SORTABLE);
942 }
943
944 /***
945 * Returns the configured list of media.
946 * @return the value of <code>PROPERTY_EXPORTTYPES</code>
947 */
948 public String[] getExportTypes()
949 {
950 String list = getProperty(PROPERTY_EXPORTTYPES);
951 if (list == null)
952 {
953 return new String[0];
954 }
955
956 return StringUtils.split(list);
957 }
958
959 /***
960 * Returns the class responsible for the given export.
961 * @param exportName export name
962 * @return String classname
963 */
964 public String getExportClass(String exportName)
965 {
966 return getProperty(PROPERTY_EXPORT_PREFIX + SEP + exportName + SEP + EXPORTPROPERTY_STRING_CLASS);
967 }
968
969 /***
970 * Returns an instance of configured requestHelperFactory.
971 * @return RequestHelperFactory instance.
972 * @throws FactoryInstantiationException if unable to load or instantiate the configurated class.
973 */
974 public RequestHelperFactory getRequestHelperFactoryInstance() throws FactoryInstantiationException
975 {
976 Object loadedObject = getClassPropertyInstance(PROPERTY_CLASS_REQUESTHELPERFACTORY);
977
978
979 if (loadedObject == null)
980 {
981 return new DefaultRequestHelperFactory();
982 }
983
984 try
985 {
986 return (RequestHelperFactory) loadedObject;
987 }
988 catch (ClassCastException e)
989 {
990 throw new FactoryInstantiationException(getClass(), PROPERTY_CLASS_REQUESTHELPERFACTORY, loadedObject
991 .getClass()
992 .getName(), e);
993 }
994 }
995
996 /***
997 * Returns the configured resource provider instance. If necessary instantiate the resource provider from config and
998 * then keep a cached instance.
999 * @return I18nResourceProvider instance.
1000 * @see I18nResourceProvider
1001 */
1002 public I18nResourceProvider geResourceProvider()
1003 {
1004 String className = getProperty(PROPERTY_CLASS_LOCALEPROVIDER);
1005
1006 if (resourceProvider == null)
1007 {
1008 if (className != null)
1009 {
1010 try
1011 {
1012 Class classProperty = ReflectHelper.classForName(className);
1013 resourceProvider = (I18nResourceProvider) classProperty.newInstance();
1014
1015 log.info(Messages.getString("TableProperties.classinitializedto",
1016 new Object[]{ClassUtils.getShortClassName(I18nResourceProvider.class), className}));
1017 }
1018 catch (Throwable e)
1019 {
1020 log.warn(Messages.getString("TableProperties.errorloading",
1021 new Object[]{
1022 ClassUtils.getShortClassName(I18nResourceProvider.class),
1023 e.getClass().getName(),
1024 e.getMessage()}));
1025 }
1026 }
1027 else
1028 {
1029 log.info(Messages.getString("TableProperties.noconfigured",
1030 new Object[]{ClassUtils.getShortClassName(I18nResourceProvider.class)}));
1031 }
1032
1033
1034 if (resourceProvider == null)
1035 {
1036
1037 resourceProvider = new I18nResourceProvider()
1038 {
1039
1040
1041 public String getResource(String titleKey, String property, Tag tag, PageContext context)
1042 {
1043 return null;
1044 }
1045 };
1046 }
1047 }
1048
1049 return resourceProvider;
1050 }
1051
1052 /***
1053 * Reads a String property.
1054 * @param key property name
1055 * @return property value or <code>null</code> if property is not found
1056 */
1057 private String getProperty(String key)
1058 {
1059 return this.properties.getProperty(key);
1060 }
1061
1062 /***
1063 * Sets a property.
1064 * @param key property name
1065 * @param value property value
1066 */
1067 public void setProperty(String key, String value)
1068 {
1069 this.properties.setProperty(key, value);
1070 }
1071
1072 /***
1073 * Reads a boolean property.
1074 * @param key property name
1075 * @return boolean <code>true</code> if the property value is "true", <code>false</code> for any other value.
1076 */
1077 private boolean getBooleanProperty(String key)
1078 {
1079 return Boolean.TRUE.toString().equals(getProperty(key));
1080 }
1081
1082 /***
1083 * Returns an instance of a configured Class. Returns a configured Class instantiated
1084 * callingClass.forName([configuration value]).
1085 * @param key configuration key
1086 * @return instance of configured class
1087 * @throws FactoryInstantiationException if unable to load or instantiate the configurated class.
1088 */
1089 private Object getClassPropertyInstance(String key) throws FactoryInstantiationException
1090 {
1091 String className = getProperty(key);
1092
1093
1094 if (className == null)
1095 {
1096 return null;
1097 }
1098
1099 try
1100 {
1101 Class classProperty = ReflectHelper.classForName(className);
1102 return classProperty.newInstance();
1103 }
1104 catch (Exception e)
1105 {
1106 throw new FactoryInstantiationException(getClass(), key, className, e);
1107 }
1108 }
1109
1110 /***
1111 * Reads an int property.
1112 * @param key property name
1113 * @param defaultValue default value returned if property is not found or not a valid int value
1114 * @return property value
1115 */
1116 private int getIntProperty(String key, int defaultValue)
1117 {
1118 try
1119 {
1120 return Integer.parseInt(getProperty(key));
1121 }
1122 catch (NumberFormatException e)
1123 {
1124
1125 log.warn(Messages.getString("TableProperties.invalidvalue",
1126 new Object[]{key, getProperty(key), new Integer(defaultValue)}));
1127 }
1128
1129 return defaultValue;
1130 }
1131 }