47 |
import gnu.classpath.Configuration; |
import gnu.classpath.Configuration; |
48 |
|
|
49 |
/** |
/** |
50 |
* A resource bundle contains locale-specific data. If you need |
* A resource bundle contains locale-specific data. If you need localized |
51 |
* localized data, you can load a resource bundle that matches the |
* data, you can load a resource bundle that matches the locale with |
52 |
* locale with <code>getBundle</code>. Now you can get your object by |
* <code>getBundle</code>. Now you can get your object by calling |
53 |
* calling <code>getObject</code> or <code>getString</code> on that |
* <code>getObject</code> or <code>getString</code> on that bundle. |
54 |
* bundle. |
* |
55 |
* <br> |
* <p>When a bundle is demanded for a specific locale, the ResourceBundle |
56 |
* When a bundle is demanded for a specific locale, the ResourceBundle |
* is searched in following order (<i>def. language<i> stands for the |
57 |
* is searched in following order (<i>def. language code<i> stands for |
* two letter ISO language code of the default locale (see |
|
* the two letter ISO language code of the default locale (see |
|
58 |
* <code>Locale.getDefault()</code>). |
* <code>Locale.getDefault()</code>). |
59 |
* <pre> |
* <pre> |
60 |
* baseName_<i>language code</i>_<i>country code</i>_<i>variant</i> |
* baseName_<i>language code</i>_<i>country code</i>_<i>variant</i> |
61 |
* baseName_<i>language code</i>_<i>country code</i> |
* baseName_<i>language code</i>_<i>country code</i> |
62 |
* baseName_<i>language code</i> |
* baseName_<i>language code</i> |
63 |
* baseName_<i>def. language code</i>_<i>def. country code</i>_<i>def. variant</i> |
* baseName_<i>def. language</i>_<i>def. country</i>_<i>def. variant</i> |
64 |
* baseName_<i>def. language code</i>_<i>def. country code</i> |
* baseName_<i>def. language</i>_<i>def. country</i> |
65 |
* baseName_<i>def. language code</i> |
* baseName_<i>def. language</i> |
66 |
* baseName |
* baseName |
67 |
* </pre> |
* </pre> |
68 |
* |
* |
69 |
* A bundle is backed up, by less specific bundle (omiting variant, |
* A bundle is backed up by less specific bundles (omiting variant, country |
70 |
* country or language). But it is not backed up by the default |
* or language). But it is not backed up by the default language locale. |
71 |
* language locale. |
* |
72 |
* <br> |
* <p>If you provide a bundle for a given locale, say |
|
* If you provide a bundle for a given locale, say |
|
73 |
* <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for |
* <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for |
74 |
* all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and |
* all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and |
75 |
* <code>Bundle</code>. |
* <code>Bundle</code>. |
|
* <br> |
|
|
* When a bundle is searched, we look first for a class with |
|
|
* the given name and if that is not found for a file with |
|
|
* <code>.properties</code> extension in the classpath. The name |
|
|
* must be a fully qualified classname (with dots as path separators). |
|
|
* <br> |
|
|
* (Note: This implementation always backs up the class with a |
|
|
* properties file if that is existing, but you shouldn't rely on |
|
|
* this, if you want to be compatible to the standard JDK.) |
|
76 |
* |
* |
77 |
|
* <p>When a bundle is searched, we look first for a class with the given |
78 |
|
* name, then for a file with <code>.properties</code> extension in the |
79 |
|
* classpath. The name must be a fully qualified classname (with dots as |
80 |
|
* path separators). |
81 |
|
* |
82 |
|
* <p>(Note: This implementation always backs up the class with a properties |
83 |
|
* file if that is existing, but you shouldn't rely on this, if you want to |
84 |
|
* be compatible to the standard JDK.) |
85 |
|
* |
86 |
|
* @author Jochen Hoenicke |
87 |
|
* @author Eric Blake <ebb9@email.byu.edu> |
88 |
* @see Locale |
* @see Locale |
89 |
|
* @see ListResourceBundle |
90 |
* @see PropertyResourceBundle |
* @see PropertyResourceBundle |
91 |
* @author Jochen Hoenicke */ |
* @since 1.1 |
92 |
|
* @status updated to 1.4 |
93 |
|
*/ |
94 |
public abstract class ResourceBundle |
public abstract class ResourceBundle |
95 |
{ |
{ |
96 |
/** |
/** |
97 |
* The parent bundle. This is consulted when you call getObject |
* The parent bundle. This is consulted when you call getObject and there |
98 |
* and there is no such resource in the current bundle. This |
* is no such resource in the current bundle. This field may be null. |
|
* field may be null. |
|
99 |
*/ |
*/ |
100 |
protected ResourceBundle parent; |
protected ResourceBundle parent; |
101 |
|
|
102 |
/** |
/** |
103 |
* The locale of this resource bundle. You can read this with |
* The locale of this resource bundle. You can read this with |
104 |
* <code>getLocale</code> and it is automatically set in |
* <code>getLocale</code> and it is automatically set in |
105 |
* <code>getBundle</code>. |
* <code>getBundle</code>. |
106 |
*/ |
*/ |
109 |
/** |
/** |
110 |
* We override SecurityManager in order to access getClassContext(). |
* We override SecurityManager in order to access getClassContext(). |
111 |
*/ |
*/ |
112 |
static class Security extends SecurityManager |
private static final class Security extends SecurityManager |
113 |
{ |
{ |
114 |
/** Return the ClassLoader of the class which called into this |
/** |
115 |
ResourceBundle, or null if it cannot be determined. */ |
* Avoid accessor method of private constructor. |
116 |
|
*/ |
117 |
|
Security() |
118 |
|
{ |
119 |
|
} |
120 |
|
|
121 |
|
/** |
122 |
|
* Return the ClassLoader of the class which called into this |
123 |
|
* ResourceBundle, or null if it cannot be determined. |
124 |
|
*/ |
125 |
ClassLoader getCallingClassLoader() |
ClassLoader getCallingClassLoader() |
126 |
{ |
{ |
127 |
Class[] stack = super.getClassContext(); |
Class[] stack = getClassContext(); |
128 |
for (int i = 0; i < stack.length; i++) |
for (int i = 0; i < stack.length; i++) |
129 |
if (stack[i] != Security.class && stack[i] != ResourceBundle.class) |
if (stack[i] != Security.class && stack[i] != ResourceBundle.class) |
130 |
return stack[i].getClassLoader(); |
return stack[i].getClassLoader(); |
131 |
return null; |
return null; |
132 |
} |
} |
133 |
} |
} |
134 |
|
|
135 |
// This will always work since java.util classes have (all) system |
/** A security context for grabbing the correct class loader. */ |
136 |
// permissions. |
private static final Security security |
137 |
static Security security = (Security) AccessController.doPrivileged |
= (Security) AccessController.doPrivileged(new PrivilegedAction() |
|
( |
|
|
new PrivilegedAction() |
|
138 |
{ |
{ |
139 |
|
// This will always work since java.util classes have (all) system |
140 |
|
// permissions. |
141 |
public Object run() |
public Object run() |
142 |
{ |
{ |
143 |
return new Security(); |
return new Security(); |
146 |
); |
); |
147 |
|
|
148 |
/** |
/** |
149 |
* The constructor. It does nothing special. |
* The resource bundle cache. This is a two-level hash map: The key |
150 |
|
* is the class loader, the value is a new HashMap. The key of this |
151 |
|
* second hash map is the localized name, the value is a soft |
152 |
|
* references to the resource bundle. |
153 |
|
*/ |
154 |
|
private static final Map resourceBundleCache = new HashMap(); |
155 |
|
|
156 |
|
/** |
157 |
|
* The `empty' locale is created once in order to optimize |
158 |
|
* tryBundle(). |
159 |
|
*/ |
160 |
|
private static final Locale emptyLocale = new Locale(""); |
161 |
|
|
162 |
|
/** |
163 |
|
* The constructor. It does nothing special. |
164 |
*/ |
*/ |
165 |
public ResourceBundle() |
public ResourceBundle() |
166 |
{ |
{ |
167 |
} |
} |
168 |
|
|
169 |
/** |
/** |
170 |
* Get a String from this resource bundle. Since most localized |
* Get a String from this resource bundle. Since most localized Objects |
171 |
* Objects are Strings, this method provides a convenient way to get |
* are Strings, this method provides a convenient way to get them without |
172 |
* them without casting. |
* casting. |
173 |
* @param key the name of the resource. |
* |
174 |
* @throws MissingResourceException |
* @param key the name of the resource |
175 |
* if that particular object could not be found in this bundle nor |
* @throws MissingResourceException if the resource can't be found |
176 |
* the parent bundle. |
* @throws NullPointerException if key is null |
177 |
|
* @throws ClassCastException if resource is not a string |
178 |
*/ |
*/ |
179 |
public final String getString(String key) throws MissingResourceException |
public final String getString(String key) |
180 |
{ |
{ |
181 |
return (String) getObject(key); |
return (String) getObject(key); |
182 |
} |
} |
183 |
|
|
184 |
/** |
/** |
185 |
* Get an array of Strings from this resource bundle. This method |
* Get an array of Strings from this resource bundle. This method |
186 |
* provides a convenient way to get it without casting. |
* provides a convenient way to get it without casting. |
187 |
* @param key the name of the resource. |
* |
188 |
* @throws MissingResourceException |
* @param key the name of the resource |
189 |
* if that particular object could not be found in this bundle nor |
* @throws MissingResourceException if the resource can't be found |
190 |
* the parent bundle. |
* @throws NullPointerException if key is null |
191 |
|
* @throws ClassCastException if resource is not a string |
192 |
*/ |
*/ |
193 |
public final String[] getStringArray(String key) |
public final String[] getStringArray(String key) |
|
throws MissingResourceException |
|
194 |
{ |
{ |
195 |
return (String[]) getObject(key); |
return (String[]) getObject(key); |
196 |
} |
} |
197 |
|
|
198 |
/** |
/** |
199 |
* Get an object from this resource bundle. |
* Get an object from this resource bundle. This will call |
200 |
* @param key the name of the resource. |
* <code>handleGetObject</code> for this resource and all of its parents, |
201 |
* @throws MissingResourceException |
* until it finds a non-null resource. |
202 |
* if that particular object could not be found in this bundle nor |
* |
203 |
* the parent bundle. |
* @param key the name of the resource |
204 |
|
* @throws MissingResourceException if the resource can't be found |
205 |
|
* @throws NullPointerException if key is null |
206 |
*/ |
*/ |
207 |
public final Object getObject(String key) throws MissingResourceException |
public final Object getObject(String key) |
208 |
{ |
{ |
209 |
for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent) |
for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent) |
210 |
{ |
try |
211 |
try |
{ |
212 |
{ |
Object o = bundle.handleGetObject(key); |
213 |
Object o = bundle.handleGetObject(key); |
if (o != null) |
214 |
if (o != null) |
return o; |
215 |
return o; |
} |
216 |
} |
catch (MissingResourceException ex) |
217 |
catch (MissingResourceException ex) |
{ |
218 |
{ |
} |
219 |
} |
throw new MissingResourceException("Key not found", |
220 |
} |
getClass().getName(), key); |
221 |
throw new MissingResourceException |
} |
222 |
("Key not found", getClass().getName(), key); |
|
223 |
|
/** |
224 |
|
* Return the actual locale of this bundle. You can use it after calling |
225 |
|
* getBundle, to know if the bundle for the desired locale was loaded or |
226 |
|
* if the fall back was used. |
227 |
|
* |
228 |
|
* @return the bundle's locale |
229 |
|
*/ |
230 |
|
public Locale getLocale() |
231 |
|
{ |
232 |
|
return locale; |
233 |
} |
} |
234 |
|
|
235 |
/** |
/** |
236 |
* Get the appropriate ResourceBundle for the default locale. |
* Set the parent of this bundle. The parent is consulted when you call |
237 |
* @param baseName the name of the ResourceBundle. This should be |
* getObject and there is no such resource in the current bundle. |
238 |
* a name of a Class or a properties-File. See the class |
* |
239 |
* description for details. |
* @param parent the parent of this bundle |
240 |
|
*/ |
241 |
|
protected void setParent(ResourceBundle parent) |
242 |
|
{ |
243 |
|
// Shall we ignore the old parent? |
244 |
|
this.parent = parent; |
245 |
|
} |
246 |
|
|
247 |
|
/** |
248 |
|
* Get the appropriate ResourceBundle for the default locale. This is like |
249 |
|
* calling <code>getBundle(baseName, Locale.getDefault(), |
250 |
|
* getClass().getClassLoader()</code>, except that any security check of |
251 |
|
* getClassLoader won't fail. |
252 |
|
* |
253 |
|
* @param baseName the name of the ResourceBundle |
254 |
* @return the desired resource bundle |
* @return the desired resource bundle |
255 |
* @throws MissingResourceException if the resource bundle can't be found |
* @throws MissingResourceException if the resource bundle can't be found |
256 |
|
* @throws NullPointerException if baseName is null |
257 |
*/ |
*/ |
258 |
public static final ResourceBundle getBundle(String baseName) |
public static final ResourceBundle getBundle(String baseName) |
|
throws MissingResourceException |
|
259 |
{ |
{ |
260 |
return getBundle(baseName, Locale.getDefault(), |
return getBundle(baseName, Locale.getDefault(), |
261 |
security.getCallingClassLoader()); |
security.getCallingClassLoader()); |
262 |
} |
} |
263 |
|
|
264 |
/** |
/** |
265 |
* Get the appropriate ResourceBundle for the given locale. |
* Get the appropriate ResourceBundle for the given locale. This is like |
266 |
* @param baseName the name of the ResourceBundle. This should be |
* calling <code>getBundle(baseName, locale, |
267 |
* a name of a Class or a properties-File. See the class |
* getClass().getClassLoader()</code>, except that any security check of |
268 |
* description for details. |
* getClassLoader won't fail. |
269 |
* @param locale A locale. |
* |
270 |
|
* @param baseName the name of the ResourceBundle |
271 |
|
* @param locale A locale |
272 |
* @return the desired resource bundle |
* @return the desired resource bundle |
273 |
* @throws MissingResourceException if the resource bundle can't be found |
* @throws MissingResourceException if the resource bundle can't be found |
274 |
|
* @throws NullPointerException if baseName or locale is null |
275 |
*/ |
*/ |
276 |
public static final ResourceBundle getBundle(String baseName, |
public static final ResourceBundle getBundle(String baseName, |
277 |
Locale locale) |
Locale locale) |
|
throws MissingResourceException |
|
278 |
{ |
{ |
279 |
return getBundle(baseName, locale, security.getCallingClassLoader()); |
return getBundle(baseName, locale, security.getCallingClassLoader()); |
280 |
} |
} |
281 |
|
|
282 |
/** |
/** |
283 |
* The resource bundle cache. This is a two-level hash map: The key |
* Get the appropriate ResourceBundle for the given locale. The following |
284 |
* is the class loader, the value is a new HashMap. The key of this |
* strategy is used: |
285 |
* second hash map is the localized name, the value is a soft |
* |
286 |
* references to the resource bundle. */ |
* <p>A sequence of candidate bundle names are generated, and tested in |
287 |
private static Map resourceBundleCache = new HashMap(); |
* this order, where the suffix 1 means the string from the specified |
288 |
|
* locale, and the suffix 2 means the string from the default locale:<ul> |
289 |
|
* <li>baseName + "_" + language1 + "_" + country1 + "_" + variant1</li> |
290 |
|
* <li>baseName + "_" + language1 + "_" + country1</li> |
291 |
|
* <li>baseName + "_" + language1</li> |
292 |
|
* <li>baseName + "_" + language2 + "_" + country2 + "_" + variant2</li> |
293 |
|
* <li>baseName + "_" + language2 + "_" + country2</li> |
294 |
|
* <li>baseName + "_" + language2<li> |
295 |
|
* <li>baseName</li> |
296 |
|
* </ul> |
297 |
|
* |
298 |
|
* <p>In the sequence, entries with an empty string are ignored. Next, |
299 |
|
* <code>getBundle</code> tries to instantiate the resource bundle:<ul> |
300 |
|
* <li>First, an attempt is made to load a class in the specified classloader |
301 |
|
* which is a subclass of ResourceBundle, and which has a public constructor |
302 |
|
* with no arguments, via reflection.</li> |
303 |
|
* <li>Next, a search is made for a property resource file, by replacing |
304 |
|
* '.' with '/' and appending ".properties", and using |
305 |
|
* ClassLoader.getResource(). If a file is found, then a |
306 |
|
* PropertyResourceBundle is created from the file's contents.</li> |
307 |
|
* </ul> |
308 |
|
* If no resource bundle was found, a MissingResourceException is thrown. |
309 |
|
* |
310 |
|
* <p>Next, the parent chain is implemented. The remaining candidate names |
311 |
|
* in the above sequence are tested in a similar manner, and if any results |
312 |
|
* in a resource bundle, it is assigned as the parent of the first bundle |
313 |
|
* using the <code>setParent</code> method (unless the first bundle already |
314 |
|
* has a parent). |
315 |
|
* |
316 |
|
* <p>For example, suppose the following class and property files are |
317 |
|
* provided: MyResources.class, MyResources_fr_CH.properties, |
318 |
|
* MyResources_fr_CH.class, MyResources_fr.properties, |
319 |
|
* MyResources_en.properties, and MyResources_es_ES.class. The contents of |
320 |
|
* all files are valid (that is, public non-abstract subclasses of |
321 |
|
* ResourceBundle with public nullary constructors for the ".class" files, |
322 |
|
* syntactically correct ".properties" files). The default locale is |
323 |
|
* Locale("en", "UK"). |
324 |
|
* |
325 |
|
* <p>Calling getBundle with the shown locale argument values instantiates |
326 |
|
* resource bundles from the following sources:<ul> |
327 |
|
* <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent |
328 |
|
* MyResources_fr.properties, parent MyResources.class</li> |
329 |
|
* <li>Locale("fr", "FR"): result MyResources_fr.properties, parent |
330 |
|
* MyResources.class</li> |
331 |
|
* <li>Locale("de", "DE"): result MyResources_en.properties, parent |
332 |
|
* MyResources.class</li> |
333 |
|
* <li>Locale("en", "US"): result MyResources_en.properties, parent |
334 |
|
* MyResources.class</li> |
335 |
|
* <li>Locale("es", "ES"): result MyResources_es_ES.class, parent |
336 |
|
* MyResources.class</li> |
337 |
|
* </ul> |
338 |
|
* The file MyResources_fr_CH.properties is never used because it is hidden |
339 |
|
* by MyResources_fr_CH.class. |
340 |
|
* |
341 |
|
* @param baseName the name of the ResourceBundle |
342 |
|
* @param locale A locale |
343 |
|
* @param classloader a ClassLoader |
344 |
|
* @return the desired resource bundle |
345 |
|
* @throws MissingResourceException if the resource bundle can't be found |
346 |
|
* @throws NullPointerException if any argument is null |
347 |
|
* @since 1.2 |
348 |
|
*/ |
349 |
|
// This method is synchronized so that the cache is properly |
350 |
|
// handled. |
351 |
|
public static final synchronized ResourceBundle getBundle |
352 |
|
(String baseName, Locale locale, ClassLoader classLoader) |
353 |
|
{ |
354 |
|
// This implementation searches the bundle in the reverse direction |
355 |
|
// and builds the parent chain on the fly. |
356 |
|
HashMap cache = (HashMap) resourceBundleCache.get(classLoader); |
357 |
|
StringBuffer sb = new StringBuffer(60); |
358 |
|
sb.append(baseName).append('_').append(locale); |
359 |
|
String name = sb.toString(); |
360 |
|
|
361 |
|
if (cache == null) |
362 |
|
{ |
363 |
|
cache = new HashMap(); |
364 |
|
resourceBundleCache.put(classLoader, cache); |
365 |
|
} |
366 |
|
else |
367 |
|
{ |
368 |
|
Reference ref = (Reference) cache.get(name); |
369 |
|
if (ref != null) |
370 |
|
{ |
371 |
|
ResourceBundle rb = (ResourceBundle) ref.get(); |
372 |
|
if (rb != null) |
373 |
|
// rb should already have the right parent, except if |
374 |
|
// something very strange happened. |
375 |
|
return rb; |
376 |
|
} |
377 |
|
} |
378 |
|
|
379 |
|
ResourceBundle baseBundle = tryBundle(baseName, emptyLocale, |
380 |
|
classLoader, null, cache); |
381 |
|
if (baseBundle == null) |
382 |
|
// JDK says, that if one provides a bundle base_en_UK, one |
383 |
|
// must also provide the bundles base_en and base. |
384 |
|
// This implies that if there is no bundle for base, there |
385 |
|
// is no bundle at all. |
386 |
|
throw new MissingResourceException("Bundle " + baseName + " not found", |
387 |
|
baseName, ""); |
388 |
|
|
389 |
|
// Now use the default locale. |
390 |
|
ResourceBundle bundle = tryLocalBundle(baseName, locale, |
391 |
|
classLoader, baseBundle, cache); |
392 |
|
if (bundle == baseBundle && !locale.equals(Locale.getDefault())) |
393 |
|
bundle = tryLocalBundle(baseName, Locale.getDefault(), |
394 |
|
classLoader, baseBundle, cache); |
395 |
|
|
396 |
|
// Check whether baseName_locale has been loaded; if not, map the |
397 |
|
// "baseName" bundle to "baseName_locale" to avoid retrying to load |
398 |
|
// baseName_locale. |
399 |
|
Reference ref = (Reference) cache.get(name); |
400 |
|
if (ref == null) |
401 |
|
cache.put(name, new SoftReference(bundle)); |
402 |
|
|
403 |
|
return bundle; |
404 |
|
} |
405 |
|
|
406 |
/** |
/** |
407 |
* The `empty' locale is created once in order to optimize |
* Override this method to provide the resource for a keys. This gets |
408 |
* tryBundle(). |
* called by <code>getObject</code>. If you don't have a resource |
409 |
|
* for the given key, you should return null instead throwing a |
410 |
|
* MissingResourceException. You don't have to ask the parent, getObject() |
411 |
|
* already does this; nor should you throw a MissingResourceException. |
412 |
|
* |
413 |
|
* @param key the key of the resource |
414 |
|
* @return the resource for the key, or null if not in bundle |
415 |
|
* @throws NullPointerException if key is null |
416 |
*/ |
*/ |
417 |
private static final Locale emptyLocale = new Locale ("", ""); |
protected abstract Object handleGetObject(String key); |
418 |
|
|
419 |
|
/** |
420 |
|
* This method should return all keys for which a resource exists; you |
421 |
|
* should include the enumeration of any parent's keys, after filtering out |
422 |
|
* duplicates. |
423 |
|
* |
424 |
|
* @return an enumeration of the keys |
425 |
|
*/ |
426 |
|
public abstract Enumeration getKeys(); |
427 |
|
|
428 |
/** |
/** |
429 |
* Tries to load a class or a property file with the specified name. |
* Tries to load a class or a property file with the specified name. |
430 |
* @param localizedName the name. |
* |
431 |
* @param locale the locale, that must be used exactly. |
* @param localizedName the name |
432 |
* @param classloader the classloader. |
* @param locale the locale, that must be used exactly |
433 |
* @param bundle the back up (parent) bundle |
* @param classloader the classloader |
434 |
* @return the resource bundle if that could be loaded, otherwise |
* @param bundle the backup (parent) bundle |
435 |
* <code>bundle</code>. |
* @return the resource bundle if it was loaded, otherwise the backup |
436 |
*/ |
*/ |
437 |
private static final ResourceBundle tryBundle(String localizedName, |
private static final ResourceBundle tryBundle(String localizedName, |
438 |
Locale locale, |
Locale locale, |
439 |
ClassLoader classloader, |
ClassLoader classloader, |
440 |
ResourceBundle bundle, |
ResourceBundle bundle, |
441 |
HashMap cache) |
HashMap cache) |
442 |
{ |
{ |
443 |
{ |
// First look into the cache. |
444 |
// First look into the cache. |
// XXX We should remove cleared references from the cache. |
445 |
// XXX We should remove cleared references from the cache. |
Reference ref = (Reference) cache.get(localizedName); |
446 |
Reference ref = (Reference) cache.get(localizedName); |
if (ref != null) |
447 |
if (ref != null) |
{ |
448 |
{ |
ResourceBundle rb = (ResourceBundle) ref.get(); |
449 |
ResourceBundle rb = (ResourceBundle) ref.get(); |
if (rb != null) |
450 |
if (rb != null) |
// rb should already have the right parent, except if |
451 |
// rb should already have the right parent, except if |
// something very strange happened. |
452 |
// something very strange happened. |
return rb; |
453 |
return rb; |
} |
|
} |
|
|
} |
|
454 |
|
|
455 |
// foundBundle holds exact matches for the localizedName resource |
// foundBundle holds exact matches for the localizedName resource |
456 |
// bundle, which may later be cached. |
// bundle, which may later be cached. |
457 |
ResourceBundle foundBundle = null; |
ResourceBundle foundBundle = null; |
458 |
try |
try |
459 |
{ |
{ |
460 |
Class rbClass; |
Class rbClass; |
461 |
if (classloader == null) |
if (classloader == null) |
462 |
rbClass = Class.forName(localizedName); |
rbClass = Class.forName(localizedName); |
463 |
else |
else |
464 |
rbClass = classloader.loadClass(localizedName); |
rbClass = classloader.loadClass(localizedName); |
465 |
foundBundle = (ResourceBundle) rbClass.newInstance(); |
foundBundle = (ResourceBundle) rbClass.newInstance(); |
466 |
foundBundle.parent = bundle; |
foundBundle.parent = bundle; |
467 |
foundBundle.locale = locale; |
foundBundle.locale = locale; |
468 |
} |
} |
469 |
catch (ClassNotFoundException ex) |
catch (Exception ex) |
470 |
{ |
{ |
471 |
} |
// ignore them all |
|
catch (IllegalAccessException ex) |
|
|
{ |
|
|
} |
|
|
catch (InstantiationException ex) |
|
|
{ |
|
|
// ignore them all |
|
|
// XXX should we also ignore ClassCastException? |
|
472 |
} |
} |
473 |
if (foundBundle == null) |
if (foundBundle == null) |
474 |
try |
try |
477 |
final String resourceName |
final String resourceName |
478 |
= localizedName.replace('.', '/') + ".properties"; |
= localizedName.replace('.', '/') + ".properties"; |
479 |
if (classloader == null) |
if (classloader == null) |
480 |
is = ClassLoader.getSystemResourceAsStream (resourceName); |
is = ClassLoader.getSystemResourceAsStream(resourceName); |
481 |
else |
else |
482 |
is = classloader.getResourceAsStream (resourceName); |
is = classloader.getResourceAsStream(resourceName); |
483 |
if (is != null) |
if (is != null) |
484 |
{ |
{ |
485 |
foundBundle = new PropertyResourceBundle(is); |
foundBundle = new PropertyResourceBundle(is); |
486 |
foundBundle.parent = bundle; |
foundBundle.parent = bundle; |
487 |
foundBundle.locale = locale; |
foundBundle.locale = locale; |
501 |
* Tries to load a the bundle for a given locale, also loads the backup |
* Tries to load a the bundle for a given locale, also loads the backup |
502 |
* locales with the same language. |
* locales with the same language. |
503 |
* |
* |
504 |
* @param name the name. |
* @param name the name |
505 |
* @param locale the locale, that must be used exactly. |
* @param locale the locale, that must be used exactly |
506 |
* @param classloader the classloader. |
* @param classloader the classloader |
507 |
* @param bundle the back up (parent) bundle |
* @param bundle the backup (parent) bundle |
508 |
* @return the resource bundle if that could be loaded, otherwise |
* @return the resource bundle if it was loaded, otherwise the backup |
|
* <code>bundle</code>. |
|
509 |
*/ |
*/ |
510 |
private static final ResourceBundle tryLocalBundle(String baseName, |
private static final ResourceBundle tryLocalBundle(String baseName, |
511 |
Locale locale, |
Locale locale, |
512 |
ClassLoader classloader, |
ClassLoader classloader, |
513 |
ResourceBundle bundle, |
ResourceBundle bundle, |
514 |
HashMap cache) |
HashMap cache) |
515 |
{ |
{ |
516 |
final String language = locale.getLanguage(); |
final String language = locale.getLanguage(); |
517 |
StringBuffer sb = new StringBuffer(60); |
StringBuffer sb = new StringBuffer(60); |
518 |
|
|
519 |
if (language.length() > 0) |
if (language.length() > 0) |
520 |
{ |
{ |
521 |
final String country = locale.getCountry(); |
final String country = locale.getCountry(); |
522 |
sb.append(baseName).append('_').append(language); |
sb.append(baseName).append('_').append(language); |
523 |
String name = sb.toString(); |
String name = sb.toString(); |
524 |
|
|
525 |
if (country.length() != 0) |
if (country.length() != 0) |
526 |
{ |
{ |
527 |
bundle = tryBundle(name, |
bundle = tryBundle(name, new Locale(language), |
528 |
new Locale(language, ""), |
classloader, bundle, cache); |
|
classloader, bundle, cache); |
|
529 |
sb.append('_').append(country); |
sb.append('_').append(country); |
530 |
name = sb.toString(); |
name = sb.toString(); |
531 |
|
|
532 |
final String variant = locale.getVariant(); |
final String variant = locale.getVariant(); |
533 |
|
|
534 |
if (variant.length() != 0) |
if (variant.length() != 0) |
535 |
{ |
{ |
536 |
bundle = tryBundle(name, |
bundle = tryBundle(name, new Locale(language, country), |
537 |
new Locale(language, |
classloader, bundle, cache); |
|
country), |
|
|
classloader, bundle, cache); |
|
538 |
sb.append('_').append(variant); |
sb.append('_').append(variant); |
539 |
name = sb.toString(); |
name = sb.toString(); |
540 |
} |
} |
541 |
} |
} |
542 |
bundle = tryBundle(name, locale, classloader, bundle, cache); |
bundle = tryBundle(name, locale, classloader, bundle, cache); |
543 |
} |
} |
544 |
return bundle; |
return bundle; |
545 |
} |
} |
546 |
|
} // class ResourceBundle |
|
/** |
|
|
* Get the appropriate ResourceBundle for the given locale. |
|
|
* @param baseName the name of the ResourceBundle. This should be |
|
|
* a name of a Class or a properties file. See the class |
|
|
* description for details. |
|
|
* @param locale A locale. |
|
|
* @param classloader a ClassLoader. |
|
|
* @return the desired resource bundle |
|
|
* @throws MissingResourceException if the resource bundle can't be found |
|
|
*/ |
|
|
// This method is synchronized so that the cache is properly |
|
|
// handled. |
|
|
public static final synchronized ResourceBundle getBundle(String baseName, |
|
|
Locale locale, |
|
|
ClassLoader classLoader) |
|
|
throws MissingResourceException |
|
|
{ |
|
|
// This implementation searches the bundle in the reverse direction |
|
|
// and builds the parent chain on the fly. |
|
|
|
|
|
HashMap cache = (HashMap) resourceBundleCache.get(classLoader); |
|
|
StringBuffer sb = new StringBuffer(60); |
|
|
sb.append(baseName).append('_').append(locale); |
|
|
String name = sb.toString(); |
|
|
|
|
|
if (cache == null) |
|
|
{ |
|
|
cache = new HashMap(); |
|
|
resourceBundleCache.put(classLoader, cache); |
|
|
} |
|
|
else |
|
|
{ |
|
|
Reference ref = (Reference) cache.get(name); |
|
|
if (ref != null) |
|
|
{ |
|
|
ResourceBundle rb = (ResourceBundle) ref.get(); |
|
|
if (rb != null) |
|
|
// rb should already have the right parent, except if |
|
|
// something very strange happened. |
|
|
return rb; |
|
|
} |
|
|
} |
|
|
|
|
|
ResourceBundle baseBundle = tryBundle(baseName, emptyLocale, |
|
|
classLoader, null, cache); |
|
|
if (baseBundle == null) |
|
|
// JDK says, that if one provides a bundle base_en_UK, one |
|
|
// must also provide the bundles base_en and base. |
|
|
// This implies that if there is no bundle for base, there |
|
|
// is no bundle at all. |
|
|
throw new MissingResourceException("Bundle " + baseName + " not found", baseName, ""); |
|
|
|
|
|
// Now use the default locale. |
|
|
ResourceBundle bundle = tryLocalBundle(baseName, locale, |
|
|
classLoader, baseBundle, cache); |
|
|
if (bundle == baseBundle && !locale.equals(Locale.getDefault())) |
|
|
{ |
|
|
bundle = tryLocalBundle(baseName, Locale.getDefault(), |
|
|
classLoader, baseBundle, cache); |
|
|
} |
|
|
|
|
|
// Check whether baseName_locale has been loaded; if not, map the |
|
|
// "baseName" bundle to "baseName_locale" to avoid retrying to load |
|
|
// baseName_locale. |
|
|
Reference ref = (Reference) cache.get(name); |
|
|
if (ref == null) |
|
|
cache.put(name, new SoftReference(bundle)); |
|
|
|
|
|
return bundle; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Return the actual locale of this bundle. You can use it after |
|
|
* calling getBundle, to know if the bundle for the desired locale |
|
|
* was loaded or if the fall back was used. |
|
|
*/ |
|
|
public Locale getLocale() |
|
|
{ |
|
|
return locale; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Set the parent of this bundle. This is consulted when you call |
|
|
* getObject and there is no such resource in the current bundle. |
|
|
* @param parent the parent of this bundle. |
|
|
*/ |
|
|
protected void setParent(ResourceBundle parent) |
|
|
{ |
|
|
// Shall we ignore the old parent? |
|
|
this.parent = parent; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Override this method to provide the resource for a keys. This gets |
|
|
* called by <code>getObject</code>. If you don't have a resource |
|
|
* for the given key, you should return null instead throwing a |
|
|
* MissingResourceException. You don't have to ask the parent, |
|
|
* getObject() already does this. |
|
|
* |
|
|
* @param key The key of the resource. |
|
|
* @return The resource for the key, or null if not in bundle. |
|
|
* @throws MissingResourceException you shouldn't throw this |
|
|
*/ |
|
|
protected abstract Object handleGetObject(String key) |
|
|
throws MissingResourceException; |
|
|
|
|
|
/** |
|
|
* This method should return all keys for which a resource exists. |
|
|
* @return An enumeration of the keys. |
|
|
*/ |
|
|
public abstract Enumeration getKeys(); |
|
|
} |
|