1 /*
2
3    Licensed to the Apache Software Foundation (ASF) under one or more
4    contributor license agreements.  See the NOTICE file distributed with
5    this work for additional information regarding copyright ownership.
6    The ASF licenses this file to You under the Apache License, Version 2.0
7    (the "License"); you may not use this file except in compliance with
8    the License.  You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17
18  */

19 package org.apache.batik.i18n;
20
21 import java.text.MessageFormat;
22 import java.util.ArrayList;
23 import java.util.Locale;
24 import java.util.List;
25 import java.util.ResourceBundle;
26 import java.util.MissingResourceException;
27
28 /**
29  * This class provides a default implementation of the Localizable interface.
30  * You can use it as a base class or as a member field and delegates various
31  * work to it.<p>
32  * For example, to implement Localizable, the following code can be used:
33  * <pre>
34  *  package mypackage;
35  *  ...
36  *  public class MyClass implements Localizable {
37  *      // This code fragment requires a file named
38  *      // 'mypackage/resources/Messages.properties', or a
39  *      // 'mypackage.resources.Messages' class which extends
40  *      // java.util.ResourceBundle, accessible using the current
41  *      // classpath.
42  *      LocalizableSupport localizableSupport =
43  *          new LocalizableSupport("mypackage.resources.Messages");
44  *
45  *      public void setLocale(Locale l) {
46  *          localizableSupport.setLocale(l);
47  *      }
48  *      public Local getLocale() {
49  *          return localizableSupport.getLocale();
50  *      }
51  *      public String formatMessage(String key, Object[] args) {
52  *          return localizableSupport.formatMessage(key, args);
53  *      }
54  *  }
55  * </pre>
56  * The algorithm for the Locale lookup in a LocalizableSupport object is:
57  * <ul>
58  *   <li>
59  *     if a Locale has been set by a call to setLocale(), use this Locale,
60  *     else,
61  *   </li>
62  *   <li>
63  *     if a Locale has been set by a call to the setDefaultLocale() method
64  *     of a LocalizableSupport object in the current LocaleGroup, use this
65  *     Locale, else,
66  *   </li>
67  *   <li>
68  *     use the object returned by Locale.getDefault() (and set by
69  *     Locale.setDefault()).
70  *   </li>
71  * </ul>
72  * This offers the possibility to have a different Locale for each object,
73  * a Locale for a group of object and/or a Locale for the JVM instance.
74  * <p>
75  * Note: if no group is specified a LocalizableSupport object belongs to a
76  * default group common to each instance of LocalizableSupport.
77  *
78  * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
79  * @version $Id: LocalizableSupport.java 1805419 2017-08-18 13:04:30Z ssteiner $
80  */

81 public class LocalizableSupport implements Localizable {
82     /**
83      * The locale group to which this object belongs.
84      */

85     protected LocaleGroup localeGroup = LocaleGroup.DEFAULT;
86
87     /**
88      * The resource bundle classname.
89      */

90     protected String bundleName;
91
92     /**
93      * The classloader to use to create the resource bundle.
94      */

95     protected ClassLoader classLoader;
96
97     /**
98      * The current locale.
99      */

100     protected Locale locale;
101
102     /**
103      * The locale in use.
104      */

105     protected Locale usedLocale;
106
107     /**
108      * The resources
109      */

110     List resourceBundles = new ArrayList();
111     Class lastResourceClass;
112
113
114     /**
115      * The class to lookup bundleName from.
116      */

117     Class cls;
118
119     /**
120      * Same as LocalizableSupport(cls, null).
121      */

122     public LocalizableSupport(String s, Class cls) {
123         this(s, cls, null);
124     }
125
126     /**
127      * Same as LocalizableSupport(cls, null).
128      */

129     public LocalizableSupport(String s, Class cls, ClassLoader cl) {
130         bundleName = s;
131         this.cls = cls;
132         classLoader = cl;
133     }
134
135     /**
136      * Same as LocalizableSupport(s, null).
137      */

138     public LocalizableSupport(String s) {
139         this(s, (ClassLoader)null);
140     }
141
142     /**
143      * Creates a new Localizable object.
144      * The resource bundle class name is required allows the use of custom
145      * classes of resource bundles.
146      * @param s  must be the name of the class to use to get the appropriate
147      *           resource bundle given the current locale.
148      * @param cl is the classloader used to create the resource bundle,
149      *           or null.
150      * @see java.util.ResourceBundle
151      */

152     public LocalizableSupport(String s, ClassLoader cl) {
153         bundleName = s;
154         classLoader = cl;
155     }
156
157     /**
158      * Implements {@link org.apache.batik.i18n.Localizable#setLocale(Locale)}.
159      */

160     public void setLocale(Locale l) {
161         if (locale != l) {
162             locale = l;
163             resourceBundles.clear();
164             lastResourceClass = null;
165         }
166     }
167
168     /**
169      * Implements {@link org.apache.batik.i18n.Localizable#getLocale()}.
170      */

171     public Locale getLocale() {
172         return locale;
173     }
174
175     /**
176      * Implements {@link
177      * org.apache.batik.i18n.ExtendedLocalizable#setLocaleGroup(LocaleGroup)}.
178      */

179     public void setLocaleGroup(LocaleGroup lg) {
180         localeGroup = lg;
181     }
182
183     /**
184      * Implements {@link
185      * org.apache.batik.i18n.ExtendedLocalizable#getLocaleGroup()}.
186      */

187     public LocaleGroup getLocaleGroup() {
188         return localeGroup;
189     }
190
191     /**
192      * Implements {@link
193      * org.apache.batik.i18n.ExtendedLocalizable#setDefaultLocale(Locale)}.
194      * Later invocations of the instance methods will lead to update the
195      * resource bundle used.
196      */

197     public void setDefaultLocale(Locale l) {
198         localeGroup.setLocale(l);
199     }
200
201     /**
202      * Implements {@link
203      * org.apache.batik.i18n.ExtendedLocalizable#getDefaultLocale()}.
204      */

205     public Locale getDefaultLocale() {
206         return localeGroup.getLocale();
207     }
208
209     /**
210      * Implements {@link
211      * org.apache.batik.i18n.Localizable#formatMessage(String,Object[])}.
212      */

213     public String formatMessage(String key, Object[] args) {
214         return MessageFormat.format(getString(key), args);
215     }
216
217     protected Locale getCurrentLocale() {
218         if (locale != nullreturn locale;
219         Locale l = localeGroup.getLocale();
220         if (l != nullreturn l;
221         return Locale.getDefault();
222     }
223
224     /**
225      * returns true if the locale is different from the previously
226      * used locale.  Also sets 'usedLocale' to the current locale.
227      */

228     protected boolean setUsedLocale() {
229         Locale l = getCurrentLocale();
230         if (usedLocale == l) return false;
231         usedLocale = l;
232         resourceBundles.clear();
233         lastResourceClass = null;
234         return true;
235     }
236
237     /**
238      * Here for backwards compatability
239      */

240     public ResourceBundle getResourceBundle() {
241         return getResourceBundle(0);
242     }
243
244     protected boolean hasNextResourceBundle(int i) {
245         if (i == 0) return true;
246         if (i < resourceBundles.size()) return true;
247
248         if (lastResourceClass == nullreturn false;
249         if (lastResourceClass == Object.classreturn false;
250         return true;
251     }
252
253     protected ResourceBundle lookupResourceBundle(String bundle,
254                                                   Class theClass){
255         ClassLoader cl = classLoader;
256         ResourceBundle rb=null;
257         if (cl != null) {
258             try {
259                 rb = ResourceBundle.getBundle(bundle, usedLocale, cl);
260             } catch (MissingResourceException mre) {
261             }
262             if (rb != null)
263                 return rb;
264         }
265
266         if (theClass != null) {
267             try {
268                 cl = theClass.getClassLoader();
269             } catch (SecurityException se) {
270             }
271         }
272         if (cl == null)
273             cl = getClass().getClassLoader();
274         try {
275             rb = ResourceBundle.getBundle(bundle, usedLocale, cl);
276         } catch (MissingResourceException mre) {
277         }
278         return rb;
279     }
280
281     protected ResourceBundle getResourceBundle(int i) {
282         setUsedLocale();
283         ResourceBundle rb=null;
284         if (cls == null) {
285             // Old behavour
286             if (resourceBundles.size() == 0) {
287                 rb = lookupResourceBundle(bundleName, null);
288                 resourceBundles.add(rb);
289             }
290             return (ResourceBundle)resourceBundles.get(0);
291         }
292
293         while (i >= resourceBundles.size()) {
294             if (lastResourceClass == Object.class)
295                 return null;
296             if (lastResourceClass == null)
297                 lastResourceClass = cls;
298             else
299                 lastResourceClass = lastResourceClass.getSuperclass();
300             Class cl = lastResourceClass;
301             String bundle = (cl.getPackage().getName() + "." + bundleName);
302             resourceBundles.add(lookupResourceBundle(bundle, cl));
303         }
304         return (ResourceBundle)resourceBundles.get(i);
305     }
306
307     /**
308      */

309     public String getString(String key) throws MissingResourceException {
310         setUsedLocale();
311         for (int i=0; hasNextResourceBundle(i); i++) {
312             ResourceBundle rb = getResourceBundle(i);
313             if (rb == nullcontinue;
314             try {
315                 String ret = rb.getString(key);
316                 if (ret != nullreturn ret;
317             } catch (MissingResourceException mre) {
318             }
319         }
320         String classStr = (cls != null)?cls.toString():bundleName;
321         throw new MissingResourceException("Unable to find resource: " + key,
322                                            classStr, key);
323     }
324
325     /**
326      * Returns the integer mapped with the given string
327      * @param key a key of the resource bundle
328      * @throws MissingResourceException if key is not the name of a resource
329      */

330     public int getInteger(String key)
331         throws MissingResourceException {
332         String i = getString(key);
333         
334         try {
335             return Integer.parseInt(i);
336         } catch (NumberFormatException e) {
337             throw new MissingResourceException
338                 ("Malformed integer", bundleName, key);
339         }
340     }
341
342     public int getCharacter(String key)
343         throws MissingResourceException {
344         String s = getString(key);
345         
346         if(s == null || s.length() == 0){
347             throw new MissingResourceException
348                 ("Malformed character", bundleName, key);
349         }
350
351         return s.charAt(0);
352     }
353 }
354