| 1 | /* ResourceBundle -- aids in loading resource bundles
|
|---|
| 2 | Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
|
|---|
| 3 |
|
|---|
| 4 | This file is part of GNU Classpath.
|
|---|
| 5 |
|
|---|
| 6 | GNU Classpath is free software; you can redistribute it and/or modify
|
|---|
| 7 | it under the terms of the GNU General Public License as published by
|
|---|
| 8 | the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 9 | any later version.
|
|---|
| 10 |
|
|---|
| 11 | GNU Classpath is distributed in the hope that it will be useful, but
|
|---|
| 12 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 14 | General Public License for more details.
|
|---|
| 15 |
|
|---|
| 16 | You should have received a copy of the GNU General Public License
|
|---|
| 17 | along with GNU Classpath; see the file COPYING. If not, write to the
|
|---|
| 18 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|---|
| 19 | 02111-1307 USA.
|
|---|
| 20 |
|
|---|
| 21 | Linking this library statically or dynamically with other modules is
|
|---|
| 22 | making a combined work based on this library. Thus, the terms and
|
|---|
| 23 | conditions of the GNU General Public License cover the whole
|
|---|
| 24 | combination.
|
|---|
| 25 |
|
|---|
| 26 | As a special exception, the copyright holders of this library give you
|
|---|
| 27 | permission to link this library with independent modules to produce an
|
|---|
| 28 | executable, regardless of the license terms of these independent
|
|---|
| 29 | modules, and to copy and distribute the resulting executable under
|
|---|
| 30 | terms of your choice, provided that you also meet, for each linked
|
|---|
| 31 | independent module, the terms and conditions of the license of that
|
|---|
| 32 | module. An independent module is a module which is not derived from
|
|---|
| 33 | or based on this library. If you modify this library, you may extend
|
|---|
| 34 | this exception to your version of the library, but you are not
|
|---|
| 35 | obligated to do so. If you do not wish to do so, delete this
|
|---|
| 36 | exception statement from your version. */
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 | package java.util;
|
|---|
| 40 |
|
|---|
| 41 | import java.lang.ref.Reference;
|
|---|
| 42 | import java.lang.ref.SoftReference;
|
|---|
| 43 | import java.io.InputStream;
|
|---|
| 44 | import java.io.IOException;
|
|---|
| 45 | import gnu.classpath.Configuration;
|
|---|
| 46 |
|
|---|
| 47 | /**
|
|---|
| 48 | * A resource bundle contains locale-specific data. If you need localized
|
|---|
| 49 | * data, you can load a resource bundle that matches the locale with
|
|---|
| 50 | * <code>getBundle</code>. Now you can get your object by calling
|
|---|
| 51 | * <code>getObject</code> or <code>getString</code> on that bundle.
|
|---|
| 52 | *
|
|---|
| 53 | * <p>When a bundle is demanded for a specific locale, the ResourceBundle
|
|---|
| 54 | * is searched in following order (<i>def. language<i> stands for the
|
|---|
| 55 | * two letter ISO language code of the default locale (see
|
|---|
| 56 | * <code>Locale.getDefault()</code>).
|
|---|
| 57 | *
|
|---|
| 58 | <pre>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</i>_<i>def. country</i>_<i>def. variant</i>
|
|---|
| 62 | baseName_<i>def. language</i>_<i>def. country</i>
|
|---|
| 63 | baseName_<i>def. language</i>
|
|---|
| 64 | baseName</pre>
|
|---|
| 65 | *
|
|---|
| 66 | * <p>A bundle is backed up by less specific bundles (omitting variant, country
|
|---|
| 67 | * or language). But it is not backed up by the default language locale.
|
|---|
| 68 | *
|
|---|
| 69 | * <p>If you provide a bundle for a given locale, say
|
|---|
| 70 | * <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for
|
|---|
| 71 | * all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and
|
|---|
| 72 | * <code>Bundle</code>.
|
|---|
| 73 | *
|
|---|
| 74 | * <p>When a bundle is searched, we look first for a class with the given
|
|---|
| 75 | * name, then for a file with <code>.properties</code> extension in the
|
|---|
| 76 | * classpath. The name must be a fully qualified classname (with dots as
|
|---|
| 77 | * path separators).
|
|---|
| 78 | *
|
|---|
| 79 | * <p>(Note: This implementation always backs up the class with a properties
|
|---|
| 80 | * file if that is existing, but you shouldn't rely on this, if you want to
|
|---|
| 81 | * be compatible to the standard JDK.)
|
|---|
| 82 | *
|
|---|
| 83 | * @author Jochen Hoenicke
|
|---|
| 84 | * @author Eric Blake ([email protected])
|
|---|
| 85 | * @see Locale
|
|---|
| 86 | * @see ListResourceBundle
|
|---|
| 87 | * @see PropertyResourceBundle
|
|---|
| 88 | * @since 1.1
|
|---|
| 89 | * @status updated to 1.4
|
|---|
| 90 | */
|
|---|
| 91 | public abstract class ResourceBundle
|
|---|
| 92 | {
|
|---|
| 93 | /**
|
|---|
| 94 | * The parent bundle. This is consulted when you call getObject and there
|
|---|
| 95 | * is no such resource in the current bundle. This field may be null.
|
|---|
| 96 | */
|
|---|
| 97 | protected ResourceBundle parent;
|
|---|
| 98 |
|
|---|
| 99 | /**
|
|---|
| 100 | * The locale of this resource bundle. You can read this with
|
|---|
| 101 | * <code>getLocale</code> and it is automatically set in
|
|---|
| 102 | * <code>getBundle</code>.
|
|---|
| 103 | */
|
|---|
| 104 | private Locale locale;
|
|---|
| 105 |
|
|---|
| 106 | private static native ClassLoader getCallingClassLoader();
|
|---|
| 107 |
|
|---|
| 108 | /**
|
|---|
| 109 | * The resource bundle cache. This is a two-level hash map: The key
|
|---|
| 110 | * is the class loader, the value is a new HashMap. The key of this
|
|---|
| 111 | * second hash map is the localized name, the value is a soft
|
|---|
| 112 | * references to the resource bundle.
|
|---|
| 113 | */
|
|---|
| 114 | private static Map resourceBundleCache;
|
|---|
| 115 |
|
|---|
| 116 | /**
|
|---|
| 117 | * The last default Locale we saw. If this ever changes then we have to
|
|---|
| 118 | * reset our caches.
|
|---|
| 119 | */
|
|---|
| 120 | private static Locale lastDefaultLocale;
|
|---|
| 121 |
|
|---|
| 122 | /**
|
|---|
| 123 | * The `empty' locale is created once in order to optimize
|
|---|
| 124 | * tryBundle().
|
|---|
| 125 | */
|
|---|
| 126 | private static final Locale emptyLocale = new Locale("");
|
|---|
| 127 |
|
|---|
| 128 | /**
|
|---|
| 129 | * The constructor. It does nothing special.
|
|---|
| 130 | */
|
|---|
| 131 | public ResourceBundle()
|
|---|
| 132 | {
|
|---|
| 133 | }
|
|---|
| 134 |
|
|---|
| 135 | /**
|
|---|
| 136 | * Get a String from this resource bundle. Since most localized Objects
|
|---|
| 137 | * are Strings, this method provides a convenient way to get them without
|
|---|
| 138 | * casting.
|
|---|
| 139 | *
|
|---|
| 140 | * @param key the name of the resource
|
|---|
| 141 | * @throws MissingResourceException if the resource can't be found
|
|---|
| 142 | * @throws NullPointerException if key is null
|
|---|
| 143 | * @throws ClassCastException if resource is not a string
|
|---|
| 144 | */
|
|---|
| 145 | public final String getString(String key)
|
|---|
| 146 | {
|
|---|
| 147 | return (String) getObject(key);
|
|---|
| 148 | }
|
|---|
| 149 |
|
|---|
| 150 | /**
|
|---|
| 151 | * Get an array of Strings from this resource bundle. This method
|
|---|
| 152 | * provides a convenient way to get it without casting.
|
|---|
| 153 | *
|
|---|
| 154 | * @param key the name of the resource
|
|---|
| 155 | * @throws MissingResourceException if the resource can't be found
|
|---|
| 156 | * @throws NullPointerException if key is null
|
|---|
| 157 | * @throws ClassCastException if resource is not a string
|
|---|
| 158 | */
|
|---|
| 159 | public final String[] getStringArray(String key)
|
|---|
| 160 | {
|
|---|
| 161 | return (String[]) getObject(key);
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | /**
|
|---|
| 165 | * Get an object from this resource bundle. This will call
|
|---|
| 166 | * <code>handleGetObject</code> for this resource and all of its parents,
|
|---|
| 167 | * until it finds a non-null resource.
|
|---|
| 168 | *
|
|---|
| 169 | * @param key the name of the resource
|
|---|
| 170 | * @throws MissingResourceException if the resource can't be found
|
|---|
| 171 | * @throws NullPointerException if key is null
|
|---|
| 172 | */
|
|---|
| 173 | public final Object getObject(String key)
|
|---|
| 174 | {
|
|---|
| 175 | for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
|
|---|
| 176 | try
|
|---|
| 177 | {
|
|---|
| 178 | Object o = bundle.handleGetObject(key);
|
|---|
| 179 | if (o != null)
|
|---|
| 180 | return o;
|
|---|
| 181 | }
|
|---|
| 182 | catch (MissingResourceException ex)
|
|---|
| 183 | {
|
|---|
| 184 | }
|
|---|
| 185 | throw new MissingResourceException("Key not found",
|
|---|
| 186 | getClass().getName(), key);
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | /**
|
|---|
| 190 | * Return the actual locale of this bundle. You can use it after calling
|
|---|
| 191 | * getBundle, to know if the bundle for the desired locale was loaded or
|
|---|
| 192 | * if the fall back was used.
|
|---|
| 193 | *
|
|---|
| 194 | * @return the bundle's locale
|
|---|
| 195 | */
|
|---|
| 196 | public Locale getLocale()
|
|---|
| 197 | {
|
|---|
| 198 | return locale;
|
|---|
| 199 | }
|
|---|
| 200 |
|
|---|
| 201 | /**
|
|---|
| 202 | * Set the parent of this bundle. The parent is consulted when you call
|
|---|
| 203 | * getObject and there is no such resource in the current bundle.
|
|---|
| 204 | *
|
|---|
| 205 | * @param parent the parent of this bundle
|
|---|
| 206 | */
|
|---|
| 207 | protected void setParent(ResourceBundle parent)
|
|---|
| 208 | {
|
|---|
| 209 | this.parent = parent;
|
|---|
| 210 | }
|
|---|
| 211 |
|
|---|
| 212 | /**
|
|---|
| 213 | * Get the appropriate ResourceBundle for the default locale. This is like
|
|---|
| 214 | * calling <code>getBundle(baseName, Locale.getDefault(),
|
|---|
| 215 | * getClass().getClassLoader()</code>, except that any security check of
|
|---|
| 216 | * getClassLoader won't fail.
|
|---|
| 217 | *
|
|---|
| 218 | * @param baseName the name of the ResourceBundle
|
|---|
| 219 | * @return the desired resource bundle
|
|---|
| 220 | * @throws MissingResourceException if the resource bundle can't be found
|
|---|
| 221 | * @throws NullPointerException if baseName is null
|
|---|
| 222 | */
|
|---|
| 223 | public static final ResourceBundle getBundle(String baseName)
|
|---|
| 224 | {
|
|---|
| 225 | return getBundle(baseName, Locale.getDefault(),
|
|---|
| 226 | getCallingClassLoader());
|
|---|
| 227 | }
|
|---|
| 228 |
|
|---|
| 229 | /**
|
|---|
| 230 | * Get the appropriate ResourceBundle for the given locale. This is like
|
|---|
| 231 | * calling <code>getBundle(baseName, locale,
|
|---|
| 232 | * getClass().getClassLoader()</code>, except that any security check of
|
|---|
| 233 | * getClassLoader won't fail.
|
|---|
| 234 | *
|
|---|
| 235 | * @param baseName the name of the ResourceBundle
|
|---|
| 236 | * @param locale A locale
|
|---|
| 237 | * @return the desired resource bundle
|
|---|
| 238 | * @throws MissingResourceException if the resource bundle can't be found
|
|---|
| 239 | * @throws NullPointerException if baseName or locale is null
|
|---|
| 240 | */
|
|---|
| 241 | public static final ResourceBundle getBundle(String baseName,
|
|---|
| 242 | Locale locale)
|
|---|
| 243 | {
|
|---|
| 244 | return getBundle(baseName, locale, getCallingClassLoader());
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | /**
|
|---|
| 248 | * Get the appropriate ResourceBundle for the given locale. The following
|
|---|
| 249 | * strategy is used:
|
|---|
| 250 | *
|
|---|
| 251 | * <p>A sequence of candidate bundle names are generated, and tested in
|
|---|
| 252 | * this order, where the suffix 1 means the string from the specified
|
|---|
| 253 | * locale, and the suffix 2 means the string from the default locale:<ul>
|
|---|
| 254 | * <li>baseName + "_" + language1 + "_" + country1 + "_" + variant1</li>
|
|---|
| 255 | * <li>baseName + "_" + language1 + "_" + country1</li>
|
|---|
| 256 | * <li>baseName + "_" + language1</li>
|
|---|
| 257 | * <li>baseName + "_" + language2 + "_" + country2 + "_" + variant2</li>
|
|---|
| 258 | * <li>baseName + "_" + language2 + "_" + country2</li>
|
|---|
| 259 | * <li>baseName + "_" + language2<li>
|
|---|
| 260 | * <li>baseName</li>
|
|---|
| 261 | * </ul>
|
|---|
| 262 | *
|
|---|
| 263 | * <p>In the sequence, entries with an empty string are ignored. Next,
|
|---|
| 264 | * <code>getBundle</code> tries to instantiate the resource bundle:<ul>
|
|---|
| 265 | * <li>First, an attempt is made to load a class in the specified classloader
|
|---|
| 266 | * which is a subclass of ResourceBundle, and which has a public constructor
|
|---|
| 267 | * with no arguments, via reflection.</li>
|
|---|
| 268 | * <li>Next, a search is made for a property resource file, by replacing
|
|---|
| 269 | * '.' with '/' and appending ".properties", and using
|
|---|
| 270 | * ClassLoader.getResource(). If a file is found, then a
|
|---|
| 271 | * PropertyResourceBundle is created from the file's contents.</li>
|
|---|
| 272 | * </ul>
|
|---|
| 273 | * If no resource bundle was found, a MissingResourceException is thrown.
|
|---|
| 274 | *
|
|---|
| 275 | * <p>Next, the parent chain is implemented. The remaining candidate names
|
|---|
| 276 | * in the above sequence are tested in a similar manner, and if any results
|
|---|
| 277 | * in a resource bundle, it is assigned as the parent of the first bundle
|
|---|
| 278 | * using the <code>setParent</code> method (unless the first bundle already
|
|---|
| 279 | * has a parent).
|
|---|
| 280 | *
|
|---|
| 281 | * <p>For example, suppose the following class and property files are
|
|---|
| 282 | * provided: MyResources.class, MyResources_fr_CH.properties,
|
|---|
| 283 | * MyResources_fr_CH.class, MyResources_fr.properties,
|
|---|
| 284 | * MyResources_en.properties, and MyResources_es_ES.class. The contents of
|
|---|
| 285 | * all files are valid (that is, public non-abstract subclasses of
|
|---|
| 286 | * ResourceBundle with public nullary constructors for the ".class" files,
|
|---|
| 287 | * syntactically correct ".properties" files). The default locale is
|
|---|
| 288 | * Locale("en", "UK").
|
|---|
| 289 | *
|
|---|
| 290 | * <p>Calling getBundle with the shown locale argument values instantiates
|
|---|
| 291 | * resource bundles from the following sources:<ul>
|
|---|
| 292 | * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent
|
|---|
| 293 | * MyResources_fr.properties, parent MyResources.class</li>
|
|---|
| 294 | * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent
|
|---|
| 295 | * MyResources.class</li>
|
|---|
| 296 | * <li>Locale("de", "DE"): result MyResources_en.properties, parent
|
|---|
| 297 | * MyResources.class</li>
|
|---|
| 298 | * <li>Locale("en", "US"): result MyResources_en.properties, parent
|
|---|
| 299 | * MyResources.class</li>
|
|---|
| 300 | * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent
|
|---|
| 301 | * MyResources.class</li>
|
|---|
| 302 | * </ul>
|
|---|
| 303 | * The file MyResources_fr_CH.properties is never used because it is hidden
|
|---|
| 304 | * by MyResources_fr_CH.class.
|
|---|
| 305 | *
|
|---|
| 306 | * @param baseName the name of the ResourceBundle
|
|---|
| 307 | * @param locale A locale
|
|---|
| 308 | * @param classloader a ClassLoader
|
|---|
| 309 | * @return the desired resource bundle
|
|---|
| 310 | * @throws MissingResourceException if the resource bundle can't be found
|
|---|
| 311 | * @throws NullPointerException if any argument is null
|
|---|
| 312 | * @since 1.2
|
|---|
| 313 | */
|
|---|
| 314 | // This method is synchronized so that the cache is properly
|
|---|
| 315 | // handled.
|
|---|
| 316 | public static final synchronized ResourceBundle getBundle
|
|---|
| 317 | (String baseName, Locale locale, ClassLoader classLoader)
|
|---|
| 318 | {
|
|---|
| 319 | // This implementation searches the bundle in the reverse direction
|
|---|
| 320 | // and builds the parent chain on the fly.
|
|---|
| 321 | Locale defaultLocale = Locale.getDefault();
|
|---|
| 322 | if (defaultLocale != lastDefaultLocale)
|
|---|
| 323 | {
|
|---|
| 324 | resourceBundleCache = new HashMap();
|
|---|
| 325 | lastDefaultLocale = defaultLocale;
|
|---|
| 326 | }
|
|---|
| 327 | HashMap cache = (HashMap) resourceBundleCache.get(classLoader);
|
|---|
| 328 | StringBuffer sb = new StringBuffer(60);
|
|---|
| 329 | sb.append(baseName).append('_').append(locale);
|
|---|
| 330 | String name = sb.toString();
|
|---|
| 331 |
|
|---|
| 332 | if (cache == null)
|
|---|
| 333 | {
|
|---|
| 334 | cache = new HashMap();
|
|---|
| 335 | resourceBundleCache.put(classLoader, cache);
|
|---|
| 336 | }
|
|---|
| 337 | else if (cache.containsKey(name))
|
|---|
| 338 | {
|
|---|
| 339 | Reference ref = (Reference) cache.get(name);
|
|---|
| 340 | ResourceBundle result = null;
|
|---|
| 341 | // If REF is null, that means that we added a `null' value to
|
|---|
| 342 | // the hash map. That means we failed to find the bundle
|
|---|
| 343 | // previously, and we cached that fact. The JDK does this, so
|
|---|
| 344 | // it must be ok.
|
|---|
| 345 | if (ref == null)
|
|---|
| 346 | throw new MissingResourceException("Bundle " + baseName
|
|---|
| 347 | + " not found",
|
|---|
| 348 | baseName, "");
|
|---|
| 349 | else
|
|---|
| 350 | {
|
|---|
| 351 | ResourceBundle rb = (ResourceBundle) ref.get();
|
|---|
| 352 | if (rb != null)
|
|---|
| 353 | {
|
|---|
| 354 | // RB should already have the right parent, except if
|
|---|
| 355 | // something very strange happened.
|
|---|
| 356 | return rb;
|
|---|
| 357 | }
|
|---|
| 358 | // If RB is null, then we previously found it but it was
|
|---|
| 359 | // collected. So we try again.
|
|---|
| 360 | }
|
|---|
| 361 | }
|
|---|
| 362 |
|
|---|
| 363 | // It is ok if this returns null. We aren't required to have the
|
|---|
| 364 | // base bundle.
|
|---|
| 365 | ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,
|
|---|
| 366 | classLoader, null, cache);
|
|---|
| 367 |
|
|---|
| 368 | // Now use our locale, followed by the default locale. We only
|
|---|
| 369 | // need to try the default locale if our locale is different, and
|
|---|
| 370 | // if our locale failed to yield a result other than the base
|
|---|
| 371 | // bundle.
|
|---|
| 372 | ResourceBundle bundle = tryLocalBundle(baseName, locale,
|
|---|
| 373 | classLoader, baseBundle, cache);
|
|---|
| 374 | if (bundle == baseBundle && !locale.equals(defaultLocale))
|
|---|
| 375 | {
|
|---|
| 376 | bundle = tryLocalBundle(baseName, defaultLocale,
|
|---|
| 377 | classLoader, baseBundle, cache);
|
|---|
| 378 | // We need to record that the argument locale maps to the
|
|---|
| 379 | // bundle we just found. If we didn't find a bundle, record
|
|---|
| 380 | // that instead.
|
|---|
| 381 | if (bundle == null)
|
|---|
| 382 | cache.put(name, null);
|
|---|
| 383 | else
|
|---|
| 384 | cache.put(name, new SoftReference(bundle));
|
|---|
| 385 | }
|
|---|
| 386 |
|
|---|
| 387 | if (bundle == null)
|
|---|
| 388 | throw new MissingResourceException("Bundle " + baseName + " not found",
|
|---|
| 389 | baseName, "");
|
|---|
| 390 |
|
|---|
| 391 | return bundle;
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | /**
|
|---|
| 395 | * Override this method to provide the resource for a keys. This gets
|
|---|
| 396 | * called by <code>getObject</code>. If you don't have a resource
|
|---|
| 397 | * for the given key, you should return null instead throwing a
|
|---|
| 398 | * MissingResourceException. You don't have to ask the parent, getObject()
|
|---|
| 399 | * already does this; nor should you throw a MissingResourceException.
|
|---|
| 400 | *
|
|---|
| 401 | * @param key the key of the resource
|
|---|
| 402 | * @return the resource for the key, or null if not in bundle
|
|---|
| 403 | * @throws NullPointerException if key is null
|
|---|
| 404 | */
|
|---|
| 405 | protected abstract Object handleGetObject(String key);
|
|---|
| 406 |
|
|---|
| 407 | /**
|
|---|
| 408 | * This method should return all keys for which a resource exists; you
|
|---|
| 409 | * should include the enumeration of any parent's keys, after filtering out
|
|---|
| 410 | * duplicates.
|
|---|
| 411 | *
|
|---|
| 412 | * @return an enumeration of the keys
|
|---|
| 413 | */
|
|---|
| 414 | public abstract Enumeration getKeys();
|
|---|
| 415 |
|
|---|
| 416 | /**
|
|---|
| 417 | * Tries to load a class or a property file with the specified name.
|
|---|
| 418 | *
|
|---|
| 419 | * @param localizedName the name
|
|---|
| 420 | * @param locale the locale, that must be used exactly
|
|---|
| 421 | * @param classloader the classloader
|
|---|
| 422 | * @param bundle the backup (parent) bundle
|
|---|
| 423 | * @return the resource bundle if it was loaded, otherwise the backup
|
|---|
| 424 | */
|
|---|
| 425 | private static final ResourceBundle tryBundle(String localizedName,
|
|---|
| 426 | Locale locale,
|
|---|
| 427 | ClassLoader classloader,
|
|---|
| 428 | ResourceBundle bundle,
|
|---|
| 429 | HashMap cache)
|
|---|
| 430 | {
|
|---|
| 431 | // First look into the cache.
|
|---|
| 432 | if (cache.containsKey(localizedName))
|
|---|
| 433 | {
|
|---|
| 434 | Reference ref = (Reference) cache.get(localizedName);
|
|---|
| 435 | ResourceBundle result = null;
|
|---|
| 436 | // If REF is null, that means that we added a `null' value to
|
|---|
| 437 | // the hash map. That means we failed to find the bundle
|
|---|
| 438 | // previously, and we cached that fact. The JDK does this, so
|
|---|
| 439 | // it must be ok.
|
|---|
| 440 | if (ref == null)
|
|---|
| 441 | return null;
|
|---|
| 442 | else
|
|---|
| 443 | {
|
|---|
| 444 | ResourceBundle rb = (ResourceBundle) ref.get();
|
|---|
| 445 | if (rb != null)
|
|---|
| 446 | {
|
|---|
| 447 | // RB should already have the right parent, except if
|
|---|
| 448 | // something very strange happened.
|
|---|
| 449 | return rb;
|
|---|
| 450 | }
|
|---|
| 451 | // If RB is null, then we previously found it but it was
|
|---|
| 452 | // collected. So we try again.
|
|---|
| 453 | }
|
|---|
| 454 | }
|
|---|
| 455 |
|
|---|
| 456 | // foundBundle holds exact matches for the localizedName resource
|
|---|
| 457 | // bundle, which may later be cached.
|
|---|
| 458 | ResourceBundle foundBundle = null;
|
|---|
| 459 | try
|
|---|
| 460 | {
|
|---|
| 461 | Class rbClass;
|
|---|
| 462 | if (classloader == null)
|
|---|
| 463 | rbClass = Class.forName(localizedName);
|
|---|
| 464 | else
|
|---|
| 465 | rbClass = classloader.loadClass(localizedName);
|
|---|
| 466 | foundBundle = (ResourceBundle) rbClass.newInstance();
|
|---|
| 467 | foundBundle.parent = bundle;
|
|---|
| 468 | foundBundle.locale = locale;
|
|---|
| 469 | }
|
|---|
| 470 | catch (Exception ex)
|
|---|
| 471 | {
|
|---|
| 472 | // ignore them all
|
|---|
| 473 | }
|
|---|
| 474 | if (foundBundle == null)
|
|---|
| 475 | {
|
|---|
| 476 | try
|
|---|
| 477 | {
|
|---|
| 478 | InputStream is;
|
|---|
| 479 | final String resourceName
|
|---|
| 480 | = localizedName.replace('.', '/') + ".properties";
|
|---|
| 481 | if (classloader == null)
|
|---|
| 482 | is = ClassLoader.getSystemResourceAsStream(resourceName);
|
|---|
| 483 | else
|
|---|
| 484 | is = classloader.getResourceAsStream(resourceName);
|
|---|
| 485 | if (is != null)
|
|---|
| 486 | {
|
|---|
| 487 | foundBundle = new PropertyResourceBundle(is);
|
|---|
| 488 | foundBundle.parent = bundle;
|
|---|
| 489 | foundBundle.locale = locale;
|
|---|
| 490 | }
|
|---|
| 491 | }
|
|---|
| 492 | catch (IOException ex)
|
|---|
| 493 | {
|
|---|
| 494 | }
|
|---|
| 495 | }
|
|---|
| 496 |
|
|---|
| 497 | // Put the result into the hash table. If we didn't find anything
|
|---|
| 498 | // here, we record our parent bundle. If we record `null' that means
|
|---|
| 499 | // nothing, not even the base, was found.
|
|---|
| 500 | if (foundBundle == null)
|
|---|
| 501 | foundBundle = bundle;
|
|---|
| 502 | if (foundBundle == null)
|
|---|
| 503 | cache.put(localizedName, null);
|
|---|
| 504 | else
|
|---|
| 505 | cache.put(localizedName, new SoftReference(foundBundle));
|
|---|
| 506 | return foundBundle;
|
|---|
| 507 | }
|
|---|
| 508 |
|
|---|
| 509 | /**
|
|---|
| 510 | * Tries to load a the bundle for a given locale, also loads the backup
|
|---|
| 511 | * locales with the same language.
|
|---|
| 512 | *
|
|---|
| 513 | * @param name the name
|
|---|
| 514 | * @param locale the locale
|
|---|
| 515 | * @param classloader the classloader
|
|---|
| 516 | * @param bundle the backup (parent) bundle
|
|---|
| 517 | * @return the resource bundle if it was loaded, otherwise the backup
|
|---|
| 518 | */
|
|---|
| 519 | private static final ResourceBundle tryLocalBundle(String baseName,
|
|---|
| 520 | Locale locale,
|
|---|
| 521 | ClassLoader classloader,
|
|---|
| 522 | ResourceBundle bundle,
|
|---|
| 523 | HashMap cache)
|
|---|
| 524 | {
|
|---|
| 525 | final String language = locale.getLanguage();
|
|---|
| 526 | final String country = locale.getCountry();
|
|---|
| 527 | final String variant = locale.getVariant();
|
|---|
| 528 |
|
|---|
| 529 | StringBuffer sb = new StringBuffer(60);
|
|---|
| 530 | sb.append(baseName);
|
|---|
| 531 | sb.append('_');
|
|---|
| 532 |
|
|---|
| 533 | if (language.length() > 0)
|
|---|
| 534 | {
|
|---|
| 535 | sb.append(language);
|
|---|
| 536 | bundle = tryBundle(sb.toString(), new Locale(language),
|
|---|
| 537 | classloader, bundle, cache);
|
|---|
| 538 | }
|
|---|
| 539 | // If LANGUAGE was empty, we still need to try the other
|
|---|
| 540 | // components, and the `_' is required.
|
|---|
| 541 | sb.append('_');
|
|---|
| 542 |
|
|---|
| 543 | if (country.length() > 0)
|
|---|
| 544 | {
|
|---|
| 545 | sb.append(country);
|
|---|
| 546 | bundle = tryBundle(sb.toString(), new Locale(language, country),
|
|---|
| 547 | classloader, bundle, cache);
|
|---|
| 548 | }
|
|---|
| 549 | sb.append('_');
|
|---|
| 550 |
|
|---|
| 551 | if (variant.length() > 0)
|
|---|
| 552 | {
|
|---|
| 553 | sb.append(variant);
|
|---|
| 554 | bundle = tryBundle(sb.toString(), locale,
|
|---|
| 555 | classloader, bundle, cache);
|
|---|
| 556 | }
|
|---|
| 557 |
|
|---|
| 558 | return bundle;
|
|---|
| 559 | }
|
|---|
| 560 | }
|
|---|