Home

This Chapter
-Chapter 7: Internationalization
-New Supported Locales
-Locale Sensitive Services SPI
-Resource Bundle Enhancement
-ResourceBundle.Control
-Summary

Table of Contents
-Introduction
-Chapter 1: Core Libraries
-Chapter 2: Dynamic Compilation
-Chapter 3: Scripting
-Chapter 4: Networking
-Chapter 5: Swing Updates
-Chapter 6: Abstract Window Toolkit
-Chapter 7: Internationalization
-Chapter 8: Java Database Connectivity 4.0
-Chapter 9: XML Digital Signature API
-Chapter 10: Streaming API for XML
-Chapter 11: Java Architecture for XML Binding
-Chapter 12: Web Services
-Chapter 13: JavaBeans Activation Framework
-Chapter 14: User-Defined MXBeans
-Chapter 15: Concurrency Updates
-Appendix A: Enums
-Appendix B: Generics
-Appendix C: Annotations

Previous
Next

 

ResourceBundle.Control

As you know, ResourceBundle is an abstract class and you create an instance of it by calling one of its static getBundle methods. These methods return an instance of a subclass and tere are two subclasses of ResourceBundle that have been present in the java.util package since JDK 1.1: ListResourceBundle and PropertyResourceBundle. ListResourceBundle is an abstract class that you extend if you want to provide key/value pairs in Java classes. Alternatively, PropertyResourceBundle is chosen if you wish to use files to store key/value pairs. Pre-Mustang, when you called getBundle with a specific locale, it would first check if an appropriately named Java class that extends ListResourceBundle can be found. If so, it would instantiate this subclass and retrieve key/value pairs from the instance. If no such class was present, getBundle would try to find an appropriate .properties file.

Mustang provides a way for you to control how getBundle works by adding new overloads that accept a ResourceBundle.Control object. Here are the new getBundle methods:

public static final ResourceBundle getBundle(java.lang.String
    baseName, Locale targetLocale, ResourceBundle.Control control)

public static final ResourceBundle getBundle(java.lang.String
    baseName, Locale targetLocale, java.lang.ClassLoader loader,
    ResourceBundle.Control control)

The first overload uses the caller’s class loader to load the bundle, the second overload uses the specified class loader.

Without a ResourceBundle.Control argument, the getBundle method always tries to find a Java class first before attempting to load a properties file if an appropriate class cannot be located. With the new getBundle methods, you can instruct getBundle to only attempt to locate a Java class only or a properties file only. If you want the getBundle method to locate a Java class only, create your ResourceBundle.Control by passing ResourceBundle.Control.FORMAT_CLASS, like this:

ResourceBundle.Control.getControl(
    ResourceBundle.Control.FORMAT_CLASS)

For example, this code creates a ResourceBundle that looks for Java classes only:

ResourceBundle.Control control = ResourceBundle.Control.getControl(
        ResourceBundle.Control.FORMAT_CLASS);
ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale,
        control);

To instruct getBundle to look for properties files only, pass the FORMAT_PROPERTIES field to getControl.

ResourceBundle.Control.getControl(
    ResourceBundle.Control.FORMAT_PROPERTIES)

Alternatively, to tell getBundle to behave in the default way, i.e. look for Java class first and then properties files, use this:

ResourceBundle.Control.getControl(
    ResourceBundle.Control.FORMAT_DEFAULT)

However, the most powerful feature offered by ResourceBundle.Control is that now you can subclass ResourceBundle.Control and write your own mechanism for loading key/value pairs. If you decide to do so, you need to override the getFormats and newBundle methods. The getFormats method should return a list of formats supported by the ResourceBundle.Control. The newBundle method returns your own instance of ResourceBundle.

The following example shows a ResourceBundle that gets its key/value pairs from XML documents, with the help of a ResourceBundle.Control subclass. First of all, note that we use the two XML files in Listings 7.7 and 7.8.

Listing 7.7: The MyResources.xml file

<?xml version="1.0" encoding="UTF-8"?>
<properties >
    <entry key="okKey">Ok</entry>
    <entry key="cancelKey">Cancel</entry>
    <entry key="submitKey">Submit</entry>
</properties>

Listing 7.8: The MyResources_de.xml file
<?xml version="1.0" encoding="UTF-8"?>
<properties >
    <entry key="cancelKey">Abbrechen</entry>
</properties>

This sample application consists of three classes. The first one is a ResourceBundle.Control subclass named RBCXml, short for resource bundle control for XML. This subclass is designed to provide a ResourceBundle that works with XML documents. The newBundle method of RBCXml returns an instance of XmlResourceBundle, the second class in this application. XmlResourceBundle retrieves key/pair values from XML documents. The last class is RBXmlDemo, the test class that puts everything together.

The RBCXml class is given in Listing 7.9.

Listing 7.9: The RBCXml class

package rbxml;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

public class RBCXml extends ResourceBundle.Control {

    // Indicates that only "xml" is supported
    public List getFormats(String baseName) {
        if (baseName == null) {
            throw new NullPointerException();
        }
        return Arrays.asList("xml");
    }

    // Create ResourceBundle
    public ResourceBundle newBundle(String baseName, Locale locale,
            String format, ClassLoader loader, boolean reload)
        throws IllegalAccessException, InstantiationException,
               IOException {

        if (baseName == null || locale == null || format == null
                || loader == null)
            throw new NullPointerException();

        ResourceBundle bundle = null;
        if (format.equals("xml")) {
            String bundleName = toBundleName(baseName, locale);
            String resourceName = toResourceName(bundleName,
                    format);
            InputStream stream = null;
            if (reload) {
                URL url = loader.getResource(resourceName);
                if (url != null) {
                    URLConnection connection = url.openConnection();
                    if (connection != null) {
                        // Disable caching
                        connection.setUseCaches(false);
                        stream = connection.getInputStream();
                    }
                }
            } else {
                stream = loader.getResourceAsStream(resourceName);
            }

            // Create XMLResourceBundle
            if (stream != null) {
                BufferedInputStream bis =
                        new BufferedInputStream(stream);
                bundle = new XMLResourceBundle(bis);
                bis.close();
            }
        }
        return bundle;
    }
}

As I mentioned previously, a child class of ResourceBundle.Control must override the newBundle method that returns an instance of ResourceBundle. As you can see in Listing 7.9, the newBundle method returns an instance of the XMLResourceBundle class, which is presented in Listing 7.10.

Listing 7.10: The XmlResourceBundle class

package rbxml;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Vector;

public class XMLResourceBundle extends ResourceBundle {
    private Properties props;

    public XMLResourceBundle(InputStream stream) throws IOException {
        props = new Properties();
        props.loadFromXML(stream);
    }

    protected Object handleGetObject(String key) {
        return props.getProperty(key);
    }

    public Enumeration<pString> getKeys() {
        Vector vString = new Vector();
        Enumeration en = props.keys();
        while (en.hasMoreElements()) {
            vString.add((String) en.nextElement());
        }
        return vString.elements();
    }
}

The XMLResourceBundle class uses the loadFromXML method of java.util.Properties to convert XML stream into key/value pairs. It also overrides two methods, handleGetObject and getKeys. handleGetObjects returns the value of the specified key from the internal Properties object. getKeys returns an enumeration of keys.

The last class, the RBXmlDemo class in Listing 7.11, demonstrates the power of this new feature.

Listing 7.11: The RBXmlDemo class

package rbxml;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;

public class RBXmlDemo {

    public static void main(String[] args) {

        String baseName = "rbxml.MyResources";
        Locale locale = Locale.GERMAN;
        ClassLoader loader = RBXmlDemo.class.getClassLoader();
        Control control = new RBCXml();

        ResourceBundle rb = ResourceBundle.getBundle(
                baseName, locale, loader, control);

        System.out.println(rb.getString("okKey"));
        System.out.println(rb.getString("cancelKey"));
        System.out.println(rb.getString("submitKey"));
    }
}

Running the RBXmlDemo class produces the following output:

Ok
Abbrechen
Submit

Previous
Next