/[classpath]/classpath/java/util/ResourceBundle.java
ViewVC logotype

Diff of /classpath/java/util/ResourceBundle.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.25 by mkoch, Fri Oct 22 18:02:06 2004 UTC revision 1.26 by bryce, Sun Nov 21 02:54:35 2004 UTC
# Line 104  public abstract class ResourceBundle Line 104  public abstract class ResourceBundle
104     * <code>getBundle</code>.     * <code>getBundle</code>.
105     */     */
106    private Locale locale;    private Locale locale;
107          
108    /**    /**
109     * We override SecurityManager in order to access getClassContext().     * We override SecurityManager in order to access getClassContext().
110     */     */
# Line 125  public abstract class ResourceBundle Line 125  public abstract class ResourceBundle
125      {      {
126        Class[] stack = getClassContext();        Class[] stack = getClassContext();
127        for (int i = 0; i < stack.length; i++)        for (int i = 0; i < stack.length; i++)
128          {         {
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          }         }
132    
133        return null;        return null;
134      }      }
# Line 138  public abstract class ResourceBundle Line 138  public abstract class ResourceBundle
138    private static final Security security    private static final Security security
139      = (Security) AccessController.doPrivileged(new PrivilegedAction()      = (Security) AccessController.doPrivileged(new PrivilegedAction()
140        {        {
141          // This will always work since java.util classes have (all) system          // This will always work since java.util classes have (all) system
142          // permissions.          // permissions.
143          public Object run()          public Object run()
144          {          {
145            return new Security();            return new Security();
146          }          }
147        }        }
148      );      );
149    
150    /**    /**
151     * The resource bundle cache. This is a two-level hash map: The key     * The resource bundle cache.
    * is the class loader, the value is a new HashMap. The key of this  
    * second hash map is the localized name, the value is a soft  
    * references to the resource bundle.  
152     */     */
153    private static Map resourceBundleCache;    private static Map bundleCache;
154    
155    /**    /**
156     * The last default Locale we saw. If this ever changes then we have to     * The last default Locale we saw. If this ever changes then we have to
# Line 215  public abstract class ResourceBundle Line 212  public abstract class ResourceBundle
212    public final Object getObject(String key)    public final Object getObject(String key)
213    {    {
214      for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)      for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
215        try        {
216          {          Object o = bundle.handleGetObject(key);
217            Object o = bundle.handleGetObject(key);          if (o != null)
218            if (o != null)            return o;
219              return o;        }
         }  
       catch (MissingResourceException ex)  
         {  
         }  
220    
221      throw new MissingResourceException("Key not found", getClass().getName(),      throw new MissingResourceException("Key not found", getClass().getName(),
222                                         key);                                         key);
# Line 263  public abstract class ResourceBundle Line 256  public abstract class ResourceBundle
256     * @throws MissingResourceException if the resource bundle can't be found     * @throws MissingResourceException if the resource bundle can't be found
257     * @throws NullPointerException if baseName is null     * @throws NullPointerException if baseName is null
258     */     */
259    public static final ResourceBundle getBundle(String baseName)    public static ResourceBundle getBundle(String baseName)
260    {    {
261      return getBundle(baseName, Locale.getDefault(),      ClassLoader cl = security.getCallingClassLoader();
262                       security.getCallingClassLoader());      if (cl == null)
263          cl = ClassLoader.getSystemClassLoader();
264        return getBundle(baseName, Locale.getDefault(), cl);
265    }    }
266    
267    /**    /**
# Line 281  public abstract class ResourceBundle Line 276  public abstract class ResourceBundle
276     * @throws MissingResourceException if the resource bundle can't be found     * @throws MissingResourceException if the resource bundle can't be found
277     * @throws NullPointerException if baseName or locale is null     * @throws NullPointerException if baseName or locale is null
278     */     */
279    public static final ResourceBundle getBundle(String baseName,    public static ResourceBundle getBundle(String baseName, Locale locale)
                                                Locale locale)  
280    {    {
281      return getBundle(baseName, locale, security.getCallingClassLoader());      ClassLoader cl = security.getCallingClassLoader();
282        if (cl == null)
283          cl = ClassLoader.getSystemClassLoader();
284        return getBundle(baseName, locale, cl);
285    }    }
286    
287      /** Cache key for the ResourceBundle cache.  Resource bundles are keyed
288          by the combination of bundle name, locale, and class loader. */
289      private static class BundleKey
290      {
291        String baseName;
292        Locale locale;
293        ClassLoader classLoader;
294        int hashcode;
295    
296        BundleKey() {}
297    
298        BundleKey(String s, Locale l, ClassLoader cl)
299        {
300          set(s, l, cl);
301        }
302        
303        void set(String s, Locale l, ClassLoader cl)
304        {
305          baseName = s;
306          locale = l;
307          classLoader = cl;
308          hashcode = baseName.hashCode() ^ locale.hashCode() ^
309            classLoader.hashCode();
310        }
311        
312        public int hashCode()
313        {
314          return hashcode;
315        }
316        
317        public boolean equals(Object o)
318        {
319          if (! (o instanceof BundleKey))
320            return false;
321          BundleKey key = (BundleKey) o;
322          return hashcode == key.hashcode &&
323            baseName.equals(key.baseName) &&
324            locale.equals(key.locale) &&
325            classLoader.equals(key.classLoader);
326        }    
327      }
328      
329      /** A cache lookup key. This avoids having to a new one for every
330       *  getBundle() call. */
331      private static BundleKey lookupKey = new BundleKey();
332      
333      /** Singleton cache entry to represent previous failed lookups. */
334      private static Object nullEntry = new Object();
335    
336    /**    /**
337     * Get the appropriate ResourceBundle for the given locale. The following     * Get the appropriate ResourceBundle for the given locale. The following
338     * strategy is used:     * strategy is used:
# Line 363  public abstract class ResourceBundle Line 409  public abstract class ResourceBundle
409     */     */
410    // This method is synchronized so that the cache is properly    // This method is synchronized so that the cache is properly
411    // handled.    // handled.
412    public static final synchronized ResourceBundle getBundle    public static synchronized ResourceBundle getBundle
413      (String baseName, Locale locale, ClassLoader classLoader)      (String baseName, Locale locale, ClassLoader classLoader)
414    {    {
415      // This implementation searches the bundle in the reverse direction      // If the default locale changed since the last time we were called,
416      // and builds the parent chain on the fly.      // all cache entries are invalidated.
417      Locale defaultLocale = Locale.getDefault();      Locale defaultLocale = Locale.getDefault();
418      if (defaultLocale != lastDefaultLocale)      if (defaultLocale != lastDefaultLocale)
419        {        {
420          resourceBundleCache = new HashMap();          bundleCache = new HashMap();
421          lastDefaultLocale = defaultLocale;          lastDefaultLocale = defaultLocale;
422        }        }
     HashMap cache = (HashMap) resourceBundleCache.get(classLoader);  
     StringBuffer sb = new StringBuffer(60);  
     sb.append(baseName).append('_').append(locale);  
     String name = sb.toString();  
423    
424      if (cache == null)      // This will throw NullPointerException if any arguments are null.
425        lookupKey.set(baseName, locale, classLoader);
426        
427        Object obj = bundleCache.get(lookupKey);
428        ResourceBundle rb = null;
429        
430        if (obj instanceof ResourceBundle)
431        {        {
432          cache = new HashMap();          return (ResourceBundle) obj;
         resourceBundleCache.put(classLoader, cache);  
433        }        }
434      else if (cache.containsKey(name))      else if (obj == nullEntry)
435        {        {
436          Reference ref = (Reference) cache.get(name);          // Lookup has failed previously. Fall through.
         // If REF is null, that means that we added a `null' value to  
         // the hash map.  That means we failed to find the bundle  
         // previously, and we cached that fact.  The JDK does this, so  
         // it must be ok.  
         if (ref == null)  
           throw new MissingResourceException("Bundle " + baseName  
                                              + " not found",  
                                              baseName, "");  
         else  
           {  
             ResourceBundle rb = (ResourceBundle) ref.get();  
             if (rb != null)  
               {  
                 // RB should already have the right parent, except if  
                 // something very strange happened.  
                 return rb;  
               }  
             // If RB is null, then we previously found it but it was  
             // collected.  So we try again.  
           }  
437        }        }
438        else
     // It is ok if this returns null.  We aren't required to have the  
     // base bundle.  
     ResourceBundle baseBundle = tryBundle(baseName, emptyLocale,  
                                           classLoader, null, cache);  
   
     // Now use our locale, followed by the default locale.  We only  
     // need to try the default locale if our locale is different, and  
     // if our locale failed to yield a result other than the base  
     // bundle.  
     ResourceBundle bundle = tryLocalBundle(baseName, locale,  
                                            classLoader, baseBundle, cache);  
     if (bundle == baseBundle && !locale.equals(defaultLocale))  
439        {        {
440          bundle = tryLocalBundle(baseName, defaultLocale,          // First, look for a bundle for the specified locale. We don't want
441                                  classLoader, baseBundle, cache);          // the base bundle this time.
442          // We need to record that the argument locale maps to the          boolean wantBase = locale.equals(defaultLocale);
443          // bundle we just found.  If we didn't find a bundle, record          ResourceBundle bundle = tryBundle(baseName, locale, classLoader,
444          // that instead.                                            wantBase);
445          if (bundle == null)  
446            cache.put(name, null);          // Try the default locale if neccessary.
447            if (bundle == null && !locale.equals(defaultLocale))
448              bundle = tryBundle(baseName, defaultLocale, classLoader, true);
449    
450            BundleKey key = new BundleKey(baseName, locale, classLoader);
451            if (bundle == null)
452              {
453                // Cache the fact that this lookup has previously failed.
454                bundleCache.put(key, nullEntry);
455              }
456          else          else
457            cache.put(name, new SoftReference(bundle));            {
458                // Cache the result and return it.
459                bundleCache.put(key, bundle);
460                return bundle;
461              }
462        }        }
463    
464      if (bundle == null)      throw new MissingResourceException("Bundle " + baseName + " not found",
465        throw new MissingResourceException("Bundle " + baseName + " not found",                                         baseName, "");
                                          baseName, "");  
   
     return bundle;  
466    }    }
467    
468    /**    /**
# Line 466  public abstract class ResourceBundle Line 491  public abstract class ResourceBundle
491     * 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.
492     *     *
493     * @param localizedName the name     * @param localizedName the name
    * @param locale the locale, that must be used exactly  
494     * @param classloader the classloader     * @param classloader the classloader
    * @param bundle the backup (parent) bundle  
495     * @return the resource bundle if it was loaded, otherwise the backup     * @return the resource bundle if it was loaded, otherwise the backup
496     */     */
497    private static ResourceBundle tryBundle(String localizedName, Locale locale,    private static ResourceBundle tryBundle(String localizedName,
498                                            ClassLoader classloader,                                            ClassLoader classloader)
                                           ResourceBundle bundle, HashMap cache)  
499    {    {
500      // First look into the cache.      ResourceBundle bundle = null;
     if (cache.containsKey(localizedName))  
       {  
         Reference ref = (Reference) cache.get(localizedName);  
         // If REF is null, that means that we added a `null' value to  
         // the hash map.  That means we failed to find the bundle  
         // previously, and we cached that fact.  The JDK does this, so  
         // it must be ok.  
         if (ref == null)  
           return null;  
         else  
           {  
             ResourceBundle rb = (ResourceBundle) ref.get();  
             if (rb != null)  
               {  
                 // RB should already have the right parent, except if  
                 // something very strange happened.  
                 return rb;  
               }  
             // If RB is null, then we previously found it but it was  
             // collected.  So we try again.  
           }  
       }  
   
     // foundBundle holds exact matches for the localizedName resource  
     // bundle, which may later be cached.  
     ResourceBundle foundBundle = null;  
501      try      try
502        {        {
503          Class rbClass;          Class rbClass;
# Line 509  public abstract class ResourceBundle Line 505  public abstract class ResourceBundle
505            rbClass = Class.forName(localizedName);            rbClass = Class.forName(localizedName);
506          else          else
507            rbClass = classloader.loadClass(localizedName);            rbClass = classloader.loadClass(localizedName);
508          foundBundle = (ResourceBundle) rbClass.newInstance();          // Note that we do the check up front instead of catching
509          foundBundle.parent = bundle;          // ClassCastException.  The reason for this is that some crazy
510          foundBundle.locale = locale;          // programs (Eclipse) have classes that do not extend
511        }          // ResourceBundle but that have the same name as a property
512      catch (Exception ex)          // bundle; in fact Eclipse relies on ResourceBundle not
513        {          // instantiating these classes.
514          // ignore them all          if (ResourceBundle.class.isAssignableFrom(rbClass))
515          foundBundle = null;            bundle = (ResourceBundle) rbClass.newInstance();
516        }        }
517      if (foundBundle == null)      catch (IllegalAccessException ex) {}
518        catch (InstantiationException ex) {}
519        catch (ClassNotFoundException ex) {}
520    
521        if (bundle == null)
522        {        {
523          try          try
524            {            {
525              InputStream is;              InputStream is;
526              final String resourceName              String resourceName
527                = localizedName.replace('.', '/') + ".properties";                = localizedName.replace('.', '/') + ".properties";
528              if (classloader == null)              if (classloader == null)
529                is = ClassLoader.getSystemResourceAsStream(resourceName);                is = ClassLoader.getSystemResourceAsStream(resourceName);
530              else              else
531                is = classloader.getResourceAsStream(resourceName);                is = classloader.getResourceAsStream(resourceName);
532              if (is != null)              if (is != null)
533                {                bundle = new PropertyResourceBundle(is);
                 foundBundle = new PropertyResourceBundle(is);  
                 foundBundle.parent = bundle;  
                 foundBundle.locale = locale;  
               }  
534            }            }
535          catch (IOException ex)          catch (IOException ex)
536            {            {
537                MissingResourceException mre = new MissingResourceException
538                  ("Failed to load bundle", localizedName, "");
539                mre.initCause(ex);
540                throw mre;
541            }            }
542        }        }
543    
544      // Put the result into the hash table.  If we didn't find anything      return bundle;
     // here, we record our parent bundle.  If we record `null' that means  
     // nothing, not even the base, was found.  
     if (foundBundle == null)  
       foundBundle = bundle;  
     if (foundBundle == null)  
       cache.put(localizedName, null);  
     else  
       cache.put(localizedName, new SoftReference(foundBundle));  
     return foundBundle;  
545    }    }
546    
547    /**    /**
548     * 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
549     * locales with the same language.     * locales with the same language.
550     *     *
551     * @param name the name     * @param baseName the raw bundle name, without locale qualifiers
552     * @param locale the locale     * @param locale the locale
553     * @param classloader the classloader     * @param classloader the classloader
554     * @param bundle the backup (parent) bundle     * @param bundle the backup (parent) bundle
555       * @param wantBase whether a resource bundle made only from the base name
556       *        (with no locale information attached) should be returned.
557     * @return the resource bundle if it was loaded, otherwise the backup     * @return the resource bundle if it was loaded, otherwise the backup
558     */     */
559    private static ResourceBundle tryLocalBundle(String baseName, Locale locale,    private static ResourceBundle tryBundle(String baseName, Locale locale,
560                                                 ClassLoader classloader,                                            ClassLoader classLoader,
561                                                 ResourceBundle bundle,                                            boolean wantBase)
562                                                 HashMap cache)    {
563    {      String language = locale.getLanguage();
564      final String language = locale.getLanguage();      String country = locale.getCountry();
565      final String country = locale.getCountry();      String variant = locale.getVariant();
566      final String variant = locale.getVariant();      
567        int baseLen = baseName.length();
568    
569        // Build up a StringBuffer containing the complete bundle name, fully
570        // qualified by locale.
571        StringBuffer sb = new StringBuffer(baseLen + variant.length() + 7);
572    
     StringBuffer sb = new StringBuffer(60);  
573      sb.append(baseName);      sb.append(baseName);
574      sb.append('_');      
   
575      if (language.length() > 0)      if (language.length() > 0)
576        {        {
577            sb.append('_');
578          sb.append(language);          sb.append(language);
579          bundle = tryBundle(sb.toString(), new Locale(language),          
580                             classloader, bundle, cache);          if (country.length() > 0)
581        }            {
582      // If LANGUAGE was empty, we still need to try the other              sb.append('_');
583      // components, and the `_' is required.              sb.append(country);
584      sb.append('_');              
585                if (variant.length() > 0)
586      if (country.length() > 0)                {
587        {                  sb.append('_');
588          sb.append(country);                  sb.append(variant);
589          bundle = tryBundle(sb.toString(), new Locale(language, country),                }
590                             classloader, bundle, cache);            }
591        }        }
     sb.append('_');  
592    
593      if (variant.length() > 0)      // Now try to load bundles, starting with the most specialized name.
594        // Build up the parent chain as we go.
595        String bundleName = sb.toString();
596        ResourceBundle first = null; // The most specialized bundle.
597        ResourceBundle last = null; // The least specialized bundle.
598        
599        while (true)
600        {        {
601          sb.append(variant);          ResourceBundle foundBundle = tryBundle(bundleName, classLoader);
602          bundle = tryBundle(sb.toString(), locale,          if (foundBundle != null)
603                             classloader, bundle, cache);            {
604                if (first == null)
605                  first = foundBundle;
606                if (last != null)
607                  last.parent = foundBundle;
608                foundBundle.locale = locale;
609                last = foundBundle;
610              }
611            int idx = bundleName.lastIndexOf('_');
612            // Try the non-localized base name only if we already have a
613            // localized child bundle, or wantBase is true.
614            if (idx > baseLen || (idx == baseLen && (first != null || wantBase)))
615              bundleName = bundleName.substring(0, idx);
616            else
617              break;
618        }        }
619        
620      return bundle;      return first;
621    }    }
622  }  }

Legend:
Removed from v.1.25  
changed lines
  Added in v.1.26

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26