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 != null) return locale;
219 Locale l = localeGroup.getLocale();
220 if (l != null) return 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 == null) return false;
249 if (lastResourceClass == Object.class) return 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 == null) continue;
314 try {
315 String ret = rb.getString(key);
316 if (ret != null) return 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