001    /*
002     * Copyright 2004-2005 Stephen J. McConnell.
003     * Copyright 1999-2004 The Apache Software Foundation
004     *
005     * Licensed  under the  Apache License,  Version 2.0  (the "License");
006     * you may not use  this file  except in  compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *   http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed  under the  License is distributed on an "AS IS" BASIS,
013     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
014     * implied.
015     *
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    package net.dpml.i18n;
020    
021    import java.text.DateFormat;
022    import java.text.MessageFormat;
023    import java.text.ParseException;
024    import java.util.Date;
025    import java.util.Locale;
026    import java.util.MissingResourceException;
027    import java.util.Random;
028    import java.util.ResourceBundle;
029    
030    /**
031     * A class to simplify extracting localized strings, icons
032     * and other common resources from a ResourceBundle.
033     *
034     * Reworked to mirror behaviour of StringManager from Tomcat (format() to getString()).
035     *
036     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
037     * @version 1.0.0
038     */
039    public class Resources
040    {
041       /**
042        * Span width constant.
043        */
044        private static final int SPAN = 100;
045    
046       /**
047        * Random seed.
048        */
049        private static final Random RANDOM = new Random();
050    
051       /**
052        * Local of Resources
053        */
054        private final Locale m_locale;
055    
056       /**
057        * Resource bundle referenced by manager
058        */
059        private ResourceBundle m_bundle;
060    
061       /**
062        * Base name of resource bundle
063        */
064        private String m_baseName;
065    
066       /**
067        * ClassLoader from which to load resources
068        */
069        private ClassLoader m_classLoader;
070    
071        /**
072         * Constructor that builds a manager in default locale.
073         *
074         * @param baseName the base name of ResourceBundle
075         */
076        public Resources( final String baseName )
077        {
078            this( baseName, Locale.getDefault(), null );
079        }
080    
081        /**
082         * Constructor that builds a manager in default locale
083         * using specified ClassLoader.
084         *
085         * @param baseName the base name of ResourceBundle
086         * @param classLoader the classLoader to load ResourceBundle from
087         */
088        public Resources( final String baseName, final ClassLoader classLoader )
089        {
090            this( baseName, Locale.getDefault(), classLoader );
091        }
092    
093        /**
094         * Constructor that builds a manager in specified locale.
095         *
096         * @param baseName the base name of ResourceBundle
097         * @param locale the Locale for resource bundle
098         */
099        public Resources( final String baseName, final Locale locale )
100        {
101            this( baseName, locale, null );
102        }
103    
104        /**
105         * Constructor that builds a manager in specified locale.
106         *
107         * @param baseName the base name of ResourceBundle
108         * @param locale the Locale for resource bundle
109         * @param classLoader the classLoader to load ResourceBundle from
110         */
111        public Resources( final String baseName,
112                          final Locale locale,
113                          final ClassLoader classLoader )
114        {
115            if( null == baseName )
116            {
117                throw new NullPointerException( "baseName property is null" );
118            }
119            if( null == locale )
120            {
121                throw new NullPointerException( "locale property is null" );
122            }
123            m_baseName = baseName;
124            m_locale = locale;
125            m_classLoader = classLoader;
126        }
127    
128        /**
129         * Retrieve a boolean from bundle.
130         *
131         * @param key the key of resource
132         * @param defaultValue the default value if key is missing
133         * @return the resource boolean
134         * @exception MissingResourceException if the requested key is unknown
135         */
136        public boolean getBoolean( final String key, final boolean defaultValue )
137            throws MissingResourceException
138        {
139            try
140            {
141                return getBoolean( key );
142            }
143            catch( final MissingResourceException mre )
144            {
145                return defaultValue;
146            }
147        }
148    
149        /**
150         * Retrieve a boolean from bundle.
151         *
152         * @param key the key of resource
153         * @return the resource boolean
154         * @exception MissingResourceException if the requested key is unknown
155         */
156        public boolean getBoolean( final String key )
157            throws MissingResourceException
158        {
159            final ResourceBundle bundle = getBundle();
160            final String value = bundle.getString( key );
161            return value.equalsIgnoreCase( "true" );
162        }
163    
164        /**
165         * Retrieve a byte from bundle.
166         *
167         * @param key the key of resource
168         * @param defaultValue the default value if key is missing
169         * @return the resource byte
170         * @exception MissingResourceException if the requested key is unknown
171         */
172        public byte getByte( final String key, final byte defaultValue )
173            throws MissingResourceException
174        {
175            try
176            {
177                return getByte( key );
178            }
179            catch( final MissingResourceException mre )
180            {
181                return defaultValue;
182            }
183        }
184    
185        /**
186         * Retrieve a byte from bundle.
187         *
188         * @param key the key of resource
189         * @return the resource byte
190         * @exception MissingResourceException if the requested key is unknown
191         */
192        public byte getByte( final String key )
193            throws MissingResourceException
194        {
195            final ResourceBundle bundle = getBundle();
196            final String value = bundle.getString( key );
197            try
198            {
199                return Byte.parseByte( value );
200            }
201            catch( final NumberFormatException nfe )
202            {
203                throw new MissingResourceException( "Expecting a byte value but got " + value,
204                                                    "java.lang.String",
205                                                    key );
206            }
207        }
208    
209        /**
210         * Retrieve a char from bundle.
211         *
212         * @param key the key of resource
213         * @param defaultValue the default value if key is missing
214         * @return the resource char
215         * @exception MissingResourceException if the requested key is unknown
216         */
217        public char getChar( final String key, final char defaultValue )
218            throws MissingResourceException
219        {
220            try
221            {
222                return getChar( key );
223            }
224            catch( final MissingResourceException mre )
225            {
226                return defaultValue;
227            }
228        }
229    
230        /**
231         * Retrieve a char from bundle.
232         *
233         * @param key the key of resource
234         * @return the resource char
235         * @exception MissingResourceException if the requested key is unknown
236         */
237        public char getChar( final String key )
238            throws MissingResourceException
239        {
240            final ResourceBundle bundle = getBundle();
241            final String value = bundle.getString( key );
242    
243            if( 1 == value.length() )
244            {
245                return value.charAt( 0 );
246            }
247            else
248            {
249                throw new MissingResourceException( "Expecting a char value but got " + value,
250                                                    "java.lang.String",
251                                                    key );
252            }
253        }
254    
255        /**
256         * Retrieve a short from bundle.
257         *
258         * @param key the key of resource
259         * @param defaultValue the default value if key is missing
260         * @return the resource short
261         * @exception MissingResourceException if the requested key is unknown
262         */
263        public short getShort( final String key, final short defaultValue )
264            throws MissingResourceException
265        {
266            try
267            {
268                return getShort( key );
269            }
270            catch( final MissingResourceException mre )
271            {
272                return defaultValue;
273            }
274        }
275    
276        /**
277         * Retrieve a short from bundle.
278         *
279         * @param key the key of resource
280         * @return the resource short
281         * @exception MissingResourceException if the requested key is unknown
282         */
283        public short getShort( final String key )
284            throws MissingResourceException
285        {
286            final ResourceBundle bundle = getBundle();
287            final String value = bundle.getString( key );
288            try
289            {
290                return Short.parseShort( value );
291            }
292            catch( final NumberFormatException nfe )
293            {
294                throw new MissingResourceException( "Expecting a short value but got " + value,
295                                                    "java.lang.String",
296                                                    key );
297            }
298        }
299    
300        /**
301         * Retrieve a integer from bundle.
302         *
303         * @param key the key of resource
304         * @param defaultValue the default value if key is missing
305         * @return the resource integer
306         * @exception MissingResourceException if the requested key is unknown
307         */
308        public int getInteger( final String key, final int defaultValue )
309            throws MissingResourceException
310        {
311            try
312            {
313                return getInteger( key );
314            }
315            catch( final MissingResourceException mre )
316            {
317                return defaultValue;
318            }
319        }
320    
321        /**
322         * Retrieve a integer from bundle.
323         *
324         * @param key the key of resource
325         * @return the resource integer
326         * @exception MissingResourceException if the requested key is unknown
327         */
328        public int getInteger( final String key )
329            throws MissingResourceException
330        {
331            final ResourceBundle bundle = getBundle();
332            final String value = bundle.getString( key );
333            try
334            {
335                return Integer.parseInt( value );
336            }
337            catch( final NumberFormatException nfe )
338            {
339                throw new MissingResourceException( "Expecting a integer value but got " + value,
340                                                    "java.lang.String",
341                                                    key );
342            }
343        }
344    
345        /**
346         * Retrieve a long from bundle.
347         *
348         * @param key the key of resource
349         * @param defaultValue the default value if key is missing
350         * @return the resource long
351         * @exception MissingResourceException if the requested key is unknown
352         */
353        public long getLong( final String key, final long defaultValue )
354            throws MissingResourceException
355        {
356            try
357            {
358                return getLong( key );
359            }
360            catch( final MissingResourceException mre )
361            {
362                return defaultValue;
363            }
364        }
365    
366        /**
367         * Retrieve a long from bundle.
368         *
369         * @param key the key of resource
370         * @return the resource long
371         * @exception MissingResourceException if the requested key is unknown
372         */
373        public long getLong( final String key )
374            throws MissingResourceException
375        {
376            final ResourceBundle bundle = getBundle();
377            final String value = bundle.getString( key );
378            try
379            {
380                return Long.parseLong( value );
381            }
382            catch( final NumberFormatException nfe )
383            {
384                throw new MissingResourceException( "Expecting a long value but got " + value,
385                                                    "java.lang.String",
386                                                    key );
387            }
388        }
389    
390        /**
391         * Retrieve a float from bundle.
392         *
393         * @param key the key of resource
394         * @param defaultValue the default value if key is missing
395         * @return the resource float
396         * @exception MissingResourceException if the requested key is unknown
397         */
398        public float getFloat( final String key, final float defaultValue )
399            throws MissingResourceException
400        {
401            try
402            {
403                return getFloat( key );
404            }
405            catch( final MissingResourceException mre )
406            {
407                return defaultValue;
408            }
409        }
410    
411        /**
412         * Retrieve a float from bundle.
413         *
414         * @param key the key of resource
415         * @return the resource float
416         * @exception MissingResourceException if the requested key is unknown
417         */
418        public float getFloat( final String key )
419            throws MissingResourceException
420        {
421            final ResourceBundle bundle = getBundle();
422            final String value = bundle.getString( key );
423            try
424            {
425                return Float.parseFloat( value );
426            }
427            catch( final NumberFormatException nfe )
428            {
429                throw new MissingResourceException( "Expecting a float value but got " + value,
430                                                    "java.lang.String",
431                                                    key );
432            }
433        }
434    
435        /**
436         * Retrieve a double from bundle.
437         *
438         * @param key the key of resource
439         * @param defaultValue the default value if key is missing
440         * @return the resource double
441         * @exception MissingResourceException if the requested key is unknown
442         */
443        public double getDouble( final String key, final double defaultValue )
444            throws MissingResourceException
445        {
446            try
447            {
448                return getDouble( key );
449            }
450            catch( final MissingResourceException mre )
451            {
452                return defaultValue;
453            }
454        }
455    
456        /**
457         * Retrieve a double from bundle.
458         *
459         * @param key the key of resource
460         * @return the resource double
461         * @exception MissingResourceException if the requested key is unknown
462         */
463        public double getDouble( final String key )
464            throws MissingResourceException
465        {
466            final ResourceBundle bundle = getBundle();
467            final String value = bundle.getString( key );
468            try
469            {
470                return Double.parseDouble( value );
471            }
472            catch( final NumberFormatException nfe )
473            {
474                throw new MissingResourceException( "Expecting a double value but got " + value,
475                                                    "java.lang.String",
476                                                    key );
477            }
478        }
479    
480        /**
481         * Retrieve a date from bundle.
482         *
483         * @param key the key of resource
484         * @param defaultValue the default value if key is missing
485         * @return the resource date
486         * @exception MissingResourceException if the requested key is unknown
487         */
488        public Date getDate( final String key, final Date defaultValue )
489            throws MissingResourceException
490        {
491            try
492            {
493                return getDate( key );
494            }
495            catch( final MissingResourceException mre )
496            {
497                return defaultValue;
498            }
499        }
500    
501        /**
502         * Retrieve a date from bundle.
503         *
504         * @param key the key of resource
505         * @return the resource date
506         * @exception MissingResourceException if the requested key is unknown
507         */
508        public Date getDate( final String key )
509            throws MissingResourceException
510        {
511            final ResourceBundle bundle = getBundle();
512            final String value = bundle.getString( key );
513            try
514            {
515                final DateFormat format =
516                    DateFormat.getDateInstance( DateFormat.DEFAULT, m_locale );
517                return format.parse( value );
518            }
519            catch( final ParseException pe )
520            {
521                throw new MissingResourceException( "Expecting a date value but got " + value,
522                                                    "java.lang.String",
523                                                    key );
524            }
525        }
526    
527        /**
528         * Retrieve a time from bundle.
529         *
530         * @param key the key of resource
531         * @param defaultValue the default value if key is missing
532         * @return the resource time
533         * @exception MissingResourceException if the requested key is unknown
534         */
535        public Date getTime( final String key, final Date defaultValue )
536            throws MissingResourceException
537        {
538            try
539            {
540                return getTime( key );
541            }
542            catch( final MissingResourceException mre )
543            {
544                return defaultValue;
545            }
546        }
547    
548        /**
549         * Retrieve a time from bundle.
550         *
551         * @param key the key of resource
552         * @return the resource time
553         * @exception MissingResourceException if the requested key is unknown
554         */
555        public Date getTime( final String key )
556            throws MissingResourceException
557        {
558            final ResourceBundle bundle = getBundle();
559            final String value = bundle.getString( key );
560            try
561            {
562                final DateFormat format =
563                    DateFormat.getTimeInstance( DateFormat.DEFAULT, m_locale );
564                return format.parse( value );
565            }
566            catch( final ParseException pe )
567            {
568                throw new MissingResourceException( "Expecting a time value but got " + value,
569                                                    "java.lang.String",
570                                                    key );
571            }
572        }
573    
574        /**
575         * Retrieve a time from bundle.
576         *
577         * @param key the key of resource
578         * @param defaultValue the default value if key is missing
579         * @return the resource time
580         * @exception MissingResourceException if the requested key is unknown
581         */
582        public Date getDateTime( final String key, final Date defaultValue )
583            throws MissingResourceException
584        {
585            try
586            {
587                return getDateTime( key );
588            }
589            catch( final MissingResourceException mre )
590            {
591                return defaultValue;
592            }
593        }
594    
595        /**
596         * Retrieve a date + time from bundle.
597         *
598         * @param key the key of resource
599         * @return the resource date + time
600         * @exception MissingResourceException if the requested key is unknown
601         */
602        public Date getDateTime( final String key )
603            throws MissingResourceException
604        {
605            final ResourceBundle bundle = getBundle();
606            final String value = bundle.getString( key );
607            try
608            {
609                final DateFormat format =
610                    DateFormat.getDateTimeInstance( DateFormat.DEFAULT, DateFormat.DEFAULT, m_locale );
611                return format.parse( value );
612            }
613            catch( final ParseException pe )
614            {
615                throw new MissingResourceException( "Expecting a time value but got " + value,
616                                                    "java.lang.String",
617                                                    key );
618            }
619        }
620    
621        /**
622         * Retrieve a raw string from bundle.
623         *
624         * @param key the key of resource
625         * @return the resource string
626         * @exception MissingResourceException if the requested key is unknown
627         */
628        public String getString( final String key )
629            throws MissingResourceException
630        {
631            final ResourceBundle bundle = getBundle();
632            return bundle.getString( key );
633        }
634    
635        /**
636         * Retrieve a string from resource bundle and format it with specified args.
637         *
638         * @param key the key for resource
639         * @param arg1 an arg
640         * @return the formatted string
641         */
642        public String getString( final String key, final Object arg1 )
643        {
644            final Object[] args = new Object[]{arg1};
645            return format( key, args );
646        }
647    
648        /**
649         * Retrieve a string from resource bundle and format it with specified args.
650         *
651         * @param key the key for resource
652         * @param arg1 an arg
653         * @param arg2 an arg
654         * @return the formatted string
655         */
656        public String getString( final String key, final Object arg1, final Object arg2 )
657        {
658            final Object[] args = new Object[]{arg1, arg2};
659            return format( key, args );
660        }
661    
662        /**
663         * Retrieve a string from resource bundle and format it with specified args.
664         *
665         * @param key the key for resource
666         * @param arg1 an arg
667         * @param arg2 an arg
668         * @param arg3 an arg
669         * @return the formatted string
670         */
671        public String getString( final String key,
672                                 final Object arg1,
673                                 final Object arg2,
674                                 final Object arg3 )
675        {
676            final Object[] args = new Object[]{arg1, arg2, arg3};
677            return format( key, args );
678        }
679    
680        /**
681         * Retrieve a string from resource bundle and format it with specified args.
682         *
683         * @param key the key for resource
684         * @param arg1 an arg
685         * @param arg2 an arg
686         * @param arg3 an arg
687         * @param arg4 an arg
688         * @return the formatted string
689         */
690        public String getString( final String key,
691                                 final Object arg1,
692                                 final Object arg2,
693                                 final Object arg3,
694                                 final Object arg4 )
695        {
696            final Object[] args = new Object[]{arg1, arg2, arg3, arg4};
697            return format( key, args );
698        }
699    
700        /**
701         * Retrieve a string from resource bundle and format it with specified args.
702         *
703         * @param key the key for resource
704         * @param arg1 an arg
705         * @param arg2 an arg
706         * @param arg3 an arg
707         * @param arg4 an arg
708         * @param arg5 an arg
709         * @return the formatted string
710         */
711        public String getString( final String key,
712                                 final Object arg1,
713                                 final Object arg2,
714                                 final Object arg3,
715                                 final Object arg4,
716                                 final Object arg5 )
717        {
718            final Object[] args = new Object[]{arg1, arg2, arg3, arg4, arg5};
719            return format( key, args );
720        }
721    
722        /**
723         * Retrieve a string from resource bundle and format it with specified args.
724         *
725         * @param key the key for resource
726         * @param arg1 an arg
727         * @param arg2 an arg
728         * @param arg3 an arg
729         * @param arg4 an arg
730         * @param arg5 an arg
731         * @param arg6 an arg
732         * @return the formatted string
733         */
734        public String getString( final String key,
735                                 final Object arg1,
736                                 final Object arg2,
737                                 final Object arg3,
738                                 final Object arg4,
739                                 final Object arg5,
740                                 final Object arg6 )
741        {
742            final Object[] args = new Object[]{arg1, arg2, arg3, arg4, arg5, arg6};
743            return format( key, args );
744        }
745    
746        /**
747         * Retrieve a string from resource bundle and format it with specified args.
748         *
749         * @param key the key for resource
750         * @param arg1 an arg
751         * @param arg2 an arg
752         * @param arg3 an arg
753         * @param arg4 an arg
754         * @param arg5 an arg
755         * @param arg6 an arg
756         * @param arg7 an arg
757         * @return the formatted string
758         */
759        public String getString( final String key,
760                                 final Object arg1,
761                                 final Object arg2,
762                                 final Object arg3,
763                                 final Object arg4,
764                                 final Object arg5,
765                                 final Object arg6,
766                                 final Object arg7 )
767        {
768            final Object[] args = new Object[]{arg1, arg2, arg3, arg4, arg5, arg6, arg7};
769            return format( key, args );
770        }
771    
772        /**
773         * Retrieve a string from resource bundle and format it with specified args.
774         *
775         * @param key the key for resource
776         * @param args an array of args
777         * @return the formatted string
778         */
779        public String format( final String key, final Object[] args )
780        {
781            try
782            {
783                final String pattern = getPatternString( key );
784                StringBuffer buff = new StringBuffer( key.length() + SPAN );
785                MessageFormat messFormat = new MessageFormat( pattern, m_locale );
786                messFormat.format( args, buff, null );
787                String result = buff.toString();
788                buff.setLength( 0 );
789                return result;
790            }
791            catch( final MissingResourceException mre )
792            {
793                final StringBuffer sb = new StringBuffer();
794                sb.append( "Unknown resource. Bundle: '" );
795                sb.append( m_baseName );
796                sb.append( "' Key: '" );
797                sb.append( key );
798                sb.append( "' Args: '" );
799    
800                for( int i = 0; i < args.length; i++ )
801                {
802                    if( 0 != i )
803                    {
804                        sb.append( "', '" );
805                    }
806                    sb.append( args[ i ] );
807                }
808    
809                sb.append( "' Reason: " );
810                sb.append( mre );
811    
812                return sb.toString();
813            }
814        }
815    
816        /**
817         * Retrieve underlying ResourceBundle.
818         * If bundle has not been loaded it will be loaded by this method.
819         * Access is given in case other resources need to be extracted
820         * that this Manager does not provide simplified access to.
821         *
822         * @return the ResourceBundle
823         * @throws MissingResourceException if an error occurs
824         */
825        public final ResourceBundle getBundle()
826            throws MissingResourceException
827        {
828            if( null == m_bundle )
829            {
830                // bundle wasn't cached, so load it, cache it, and return it.
831                ClassLoader classLoader = m_classLoader;
832                if( null == classLoader )
833                {
834                    classLoader = Thread.currentThread().getContextClassLoader();
835                }
836                if( null != classLoader )
837                {
838                    m_bundle = ResourceBundle.getBundle( m_baseName, m_locale, classLoader );
839                }
840                else
841                {
842                    m_bundle = ResourceBundle.getBundle( m_baseName, m_locale );
843                }
844            }
845            return m_bundle;
846        }
847    
848        /**
849         * Utility method to retrieve a string from ResourceBundle.
850         * If the key is a single string then that will be returned.
851         * If key refers to string array then a random string will be chosen.
852         * Other types cause an exception.
853         *
854         * @param key the key to resource
855         * @return the string resource
856         * @throws MissingResourceException if an error occurs
857         */
858        private String getPatternString( final String key )
859            throws MissingResourceException
860        {
861            final ResourceBundle bundle = getBundle();
862            final Object object = bundle.getObject( key );
863    
864            // is the resource a single string
865            if( object instanceof String )
866            {
867                return (String) object;
868            }
869            else if( object instanceof String[] )
870            {
871                //if string array then randomly pick one
872                final String[] strings = (String[]) object;
873                return strings[ RANDOM.nextInt( strings.length ) ];
874            }
875            else
876            {
877                throw new MissingResourceException( "Unable to find resource of appropriate type.",
878                                                    "java.lang.String",
879                                                    key );
880            }
881        }
882    }