source: trunk/src/gcc/libjava/java/util/ResourceBundle.java@ 3

Last change on this file since 3 was 2, checked in by bird, 23 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.1 KB
Line 
1/* java.util.ResourceBundle
2 Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38
39package java.util;
40import java.lang.ref.Reference;
41import java.lang.ref.SoftReference;
42import java.security.AccessController;
43import java.security.PrivilegedAction;
44import gnu.classpath.Configuration;
45
46/**
47 * A resource bundle contains locale-specific data. If you need
48 * localized data, you can load a resource bundle that matches the
49 * locale with <code>getBundle</code>. Now you can get your object by
50 * calling <code>getObject</code> or <code>getString</code> on that
51 * bundle.
52 * <br>
53 * When a bundle is demanded for a specific locale, the ResourceBundle
54 * is searched in following order (<i>def. language code<i> stands for
55 * the two letter ISO language code of the default locale (see
56 * <code>Locale.getDefault()</code>).
57 * <pre>
58 * baseName_<i>language code</i>_<i>country code</i>_<i>variant</i>
59 * baseName_<i>language code</i>_<i>country code</i>
60 * baseName_<i>language code</i>
61 * baseName_<i>def. language code</i>_<i>def. country code</i>_<i>def. variant</i>
62 * baseName_<i>def. language code</i>_<i>def. country code</i>
63 * baseName_<i>def. language code</i>
64 * baseName
65 * </pre>
66 *
67 * A bundle is backed up, by less specific bundle (omiting variant,
68 * country or language). But it is not backed up by the default
69 * language locale.
70 * <br>
71 * If you provide a bundle for a given locale, say
72 * <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for
73 * all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and
74 * <code>Bundle</code>.
75 * <br>
76 * When a bundle is searched, we look first for a class with
77 * the given name and if that is not found for a file with
78 * <code>.properties</code> extension in the classpath. The name
79 * must be a fully qualified classname (with dots as path separators).
80 * <br>
81 * (Note: This implementation always backs up the class with a
82 * properties file if that is existing, but you shouldn't rely on
83 * this, if you want to be compatible to the standard JDK.)
84 *
85 * @see Locale
86 * @see PropertyResourceBundle
87 * @author Jochen Hoenicke */
88public abstract class ResourceBundle
89{
90 /**
91 * The parent bundle. This is consulted when you call getObject
92 * and there is no such resource in the current bundle. This
93 * field may be null.
94 */
95 protected ResourceBundle parent;
96
97 /**
98 * The locale of this resource bundle. You can read this with
99 * <code>getLocale</code> and it is automatically set in
100 * <code>getBundle</code>.
101 */
102 private Locale locale;
103
104 /**
105 * We override SecurityManager in order to access getClassContext().
106 */
107 static class Security extends SecurityManager
108 {
109 /** Return the ClassLoader of the class which called into this
110 ResourceBundle, or null if it cannot be determined. */
111 ClassLoader getCallingClassLoader()
112 {
113 Class[] stack = super.getClassContext();
114 for (int i = 0; i < stack.length; i++)
115 if (stack[i] != Security.class && stack[i] != ResourceBundle.class)
116 return stack[i].getClassLoader();
117 return null;
118 }
119 }
120
121 // This will always work since java.util classes have (all) system
122 // permissions.
123 static Security security = (Security) AccessController.doPrivileged
124 (
125 new PrivilegedAction()
126 {
127 public Object run()
128 {
129 return new Security();
130 }
131 }
132 );
133
134 /**
135 * The constructor. It does nothing special.
136 */
137 public ResourceBundle()
138 {
139 }
140
141 /**
142 * Get a String from this resource bundle. Since most localized
143 * Objects are Strings, this method provides a convenient way to get
144 * them without casting.
145 * @param key the name of the resource.
146 * @exception MissingResourceException
147 * if that particular object could not be found in this bundle nor
148 * the parent bundle.
149 */
150 public final String getString(String key) throws MissingResourceException
151 {
152 return (String) getObject(key);
153 }
154
155 /**
156 * Get an array of Strings from this resource bundle. This method
157 * provides a convenient way to get it without casting.
158 * @param key the name of the resource.
159 * @exception MissingResourceException
160 * if that particular object could not be found in this bundle nor
161 * the parent bundle.
162 */
163 public final String[] getStringArray(String key)
164 throws MissingResourceException
165 {
166 return (String[]) getObject(key);
167 }
168
169 /**
170 * Get an object from this resource bundle.
171 * @param key the name of the resource.
172 * @exception MissingResourceException
173 * if that particular object could not be found in this bundle nor
174 * the parent bundle.
175 */
176 public final Object getObject(String key) throws MissingResourceException
177 {
178 for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
179 {
180 try
181 {
182 Object o = bundle.handleGetObject(key);
183 if (o != null)
184 return o;
185 }
186 catch (MissingResourceException ex)
187 {
188 }
189 }
190 throw new MissingResourceException
191 ("Key not found", getClass().getName(), key);
192 }
193
194 /**
195 * Get the appropriate ResourceBundle for the default locale.
196 * @param baseName the name of the ResourceBundle. This should be
197 * a name of a Class or a properties-File. See the class
198 * description for details.
199 * @return the desired resource bundle
200 * @exception MissingResourceException
201 * if the resource bundle couldn't be found.
202 */
203 public static final ResourceBundle getBundle(String baseName)
204 throws MissingResourceException
205 {
206 return getBundle(baseName, Locale.getDefault(),
207 security.getCallingClassLoader());
208 }
209
210 /**
211 * Get the appropriate ResourceBundle for the given locale.
212 * @param baseName the name of the ResourceBundle. This should be
213 * a name of a Class or a properties-File. See the class
214 * description for details.
215 * @param locale A locale.
216 * @return the desired resource bundle
217 * @exception MissingResourceException
218 * if the resource bundle couldn't be found.
219 */
220 public static final ResourceBundle getBundle(String baseName,
221 Locale locale)
222 throws MissingResourceException
223 {
224 return getBundle(baseName, locale, security.getCallingClassLoader());
225 }
226
227 /**
228 * The resource bundle cache. This is a two-level hash map: The key
229 * is the class loader, the value is a new HashMap. The key of this
230 * second hash map is the localized name, the value is a soft
231 * references to the resource bundle. */
232 private static Map resourceBundleCache = new HashMap();
233
234 /**
235 * The `empty' locale is created once in order to optimize
236 * tryBundle().
237 */
238 private static final Locale emptyLocale = new Locale ("", "");
239
240 /**
241 * Tries to load a class or a property file with the specified name.
242 * @param localizedName the name.
243 * @param locale the locale, that must be used exactly.
244 * @param classloader the classloader.
245 * @param bundle the back up (parent) bundle
246 * @return the resource bundle if that could be loaded, otherwise
247 * <code>bundle</code>.
248 */
249 private static final ResourceBundle tryBundle(String localizedName,
250 Locale locale,
251 ClassLoader classloader,
252 ResourceBundle bundle,
253 HashMap cache)
254 {
255 {
256 // First look into the cache.
257 // XXX We should remove cleared references from the cache.
258 Reference ref = (Reference) cache.get(localizedName);
259 if (ref != null)
260 {
261 ResourceBundle rb = (ResourceBundle) ref.get();
262 if (rb != null)
263 // rb should already have the right parent, except if
264 // something very strange happened.
265 return rb;
266 }
267 }
268
269 // foundBundle holds exact matches for the localizedName resource
270 // bundle, which may later be cached.
271 ResourceBundle foundBundle = null;
272
273 try
274 {
275 java.io.InputStream is;
276 final String resourceName =
277 localizedName.replace('.', '/') + ".properties";
278 if (classloader == null)
279 is = ClassLoader.getSystemResourceAsStream (resourceName);
280 else
281 is = classloader.getResourceAsStream (resourceName);
282 if (is != null)
283 {
284 foundBundle = new PropertyResourceBundle(is);
285 foundBundle.parent = bundle;
286 foundBundle.locale = locale;
287 }
288 }
289 catch (java.io.IOException ex)
290 {
291 }
292
293 try
294 {
295 Class rbClass;
296 if (classloader == null)
297 rbClass = Class.forName(localizedName);
298 else
299 rbClass = classloader.loadClass(localizedName);
300 foundBundle = (ResourceBundle) rbClass.newInstance();
301 foundBundle.parent = bundle;
302 foundBundle.locale = locale;
303 }
304 catch (ClassNotFoundException ex)
305 {
306 }
307 catch (IllegalAccessException ex)
308 {
309 }
310 catch (InstantiationException ex)
311 {
312 // ignore them all
313 // XXX should we also ignore ClassCastException?
314 }
315
316 if (foundBundle != null)
317 cache.put(localizedName, new SoftReference(foundBundle));
318
319 return foundBundle != null ? foundBundle : bundle;
320 }
321
322 /**
323 * Tries to load a the bundle for a given locale, also loads the backup
324 * locales with the same language.
325 *
326 * @param name the name.
327 * @param locale the locale, that must be used exactly.
328 * @param classloader the classloader.
329 * @param bundle the back up (parent) bundle
330 * @return the resource bundle if that could be loaded, otherwise
331 * <code>bundle</code>.
332 */
333 private static final ResourceBundle tryLocalBundle(String baseName,
334 Locale locale,
335 ClassLoader classloader,
336 ResourceBundle bundle,
337 HashMap cache)
338 {
339 final String language = locale.getLanguage();
340
341 if (language.length() > 0)
342 {
343 final String country = locale.getCountry();
344 String name = baseName + "_" + language;
345
346 if (country.length() != 0)
347 {
348 bundle = tryBundle(name,
349 new Locale(language, ""),
350 classloader, bundle, cache);
351
352 name += "_" + country;
353
354 final String variant = locale.getVariant();
355
356 if (variant.length() != 0)
357 {
358 bundle = tryBundle(name,
359 new Locale(language,
360 country),
361 classloader, bundle, cache);
362
363 name += "_" + variant;
364 }
365 }
366 bundle = tryBundle(name, locale, classloader, bundle, cache);
367 }
368 return bundle;
369 }
370
371 /**
372 * Get the appropriate ResourceBundle for the given locale.
373 * @param baseName the name of the ResourceBundle. This should be
374 * a name of a Class or a properties file. See the class
375 * description for details.
376 * @param locale A locale.
377 * @param classloader a ClassLoader.
378 * @return the desired resource bundle
379 * @exception MissingResourceException
380 * if the resource bundle couldn't be found.
381 */
382 // This method is synchronized so that the cache is properly
383 // handled.
384 public static final synchronized ResourceBundle getBundle(String baseName,
385 Locale locale,
386 ClassLoader classLoader)
387 throws MissingResourceException
388 {
389 // This implementation searches the bundle in the reverse direction
390 // and builds the parent chain on the fly.
391
392 HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
393 if (cache == null)
394 {
395 cache = new HashMap();
396 resourceBundleCache.put(classLoader, cache);
397 }
398 else
399 {
400 // Fast path: If baseName + "_" + locale is in cache use it.
401 String name = baseName + "_" + locale.toString();
402 Reference ref = (Reference) cache.get(name);
403 if (ref != null)
404 {
405 ResourceBundle rb = (ResourceBundle) ref.get();
406 if (rb != null)
407 // rb should already have the right parent, except if
408 // something very strange happened.
409 return rb;
410 }
411 }
412
413 ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,
414 classLoader, null, cache);
415 if (baseBundle == null)
416 // JDK says, that if one provides a bundle base_en_UK, one
417 // must also provide the bundles base_en and base.
418 // This implies that if there is no bundle for base, there
419 // is no bundle at all.
420 throw new MissingResourceException("Bundle " + baseName + " not found", baseName, "");
421
422 // Now use the default locale.
423 ResourceBundle bundle = tryLocalBundle(baseName, locale,
424 classLoader, baseBundle, cache);
425 if (bundle == baseBundle && !locale.equals(Locale.getDefault()))
426 {
427 bundle = tryLocalBundle(baseName, Locale.getDefault(),
428 classLoader, baseBundle, cache);
429 }
430 return bundle;
431 }
432
433 /**
434 * Return the actual locale of this bundle. You can use it after
435 * calling getBundle, to know if the bundle for the desired locale
436 * was loaded or if the fall back was used.
437 */
438 public Locale getLocale()
439 {
440 return locale;
441 }
442
443 /**
444 * Set the parent of this bundle. This is consulted when you call
445 * getObject and there is no such resource in the current bundle.
446 * @param parent the parent of this bundle.
447 */
448 protected void setParent(ResourceBundle parent)
449 {
450 // Shall we ignore the old parent?
451 this.parent = parent;
452 }
453
454 /**
455 * Override this method to provide the resource for a keys. This gets
456 * called by <code>getObject</code>. If you don't have a resource
457 * for the given key, you should return null instead throwing a
458 * MissingResourceException. You don't have to ask the parent,
459 * getObject() already does this.
460 *
461 * @param key The key of the resource.
462 * @return The resource for the key, or null if not in bundle.
463 * @exception MissingResourceException
464 * you shouldn't throw this.
465 */
466 protected abstract Object handleGetObject(String key)
467 throws MissingResourceException;
468
469 /**
470 * This method should return all keys for which a resource exists.
471 * @return An enumeration of the keys.
472 */
473 public abstract Enumeration getKeys();
474}
Note: See TracBrowser for help on using the repository browser.