dk.statsbiblioteket.util.i18n.BundleCache.java Source code

Java tutorial

Introduction

Here is the source code for dk.statsbiblioteket.util.i18n.BundleCache.java

Source

/* $Id: BundleCache.java,v 1.8 2007/12/04 13:22:01 mke Exp $
 * $Revision: 1.8 $
 * $Date: 2007/12/04 13:22:01 $
 * $Author: mke $
 *
 * The SB Util Library.
 * Copyright (C) 2005-2007  The State and University Library of Denmark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package dk.statsbiblioteket.util.i18n;

import dk.statsbiblioteket.util.qa.QAInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Package private class to help cache {@link ResourceBundle}s. This is needed
 * because the whole {@link Properties}/ResourceBundle setup is fundamentally
 * broken, in that it is impossible to handle property files in UTF-8.
 * Go figure.
 *
 * To make it explicit: With this class you can keep your resource bundles
 * in UTF-8 encoding.
 *
 * You would not need to use this class normally. It is used automatically by
 * {@link Translator}.
 *
 * @see Translator
 * @see ResourceBundle
 */
@QAInfo(state = QAInfo.State.QA_NEEDED, level = QAInfo.Level.NORMAL)
public class BundleCache {

    private Log log;
    private Map<String, ResourceBundle> cache;
    private static BundleCache self;

    private BundleCache() {
        cache = new HashMap<String, ResourceBundle>();
        log = LogFactory.getLog(BundleCache.class);
    }

    public static BundleCache getInstance() {
        if (self == null) {
            self = new BundleCache();
            if (self.log.isDebugEnabled()) {
                self.log.debug("Created " + BundleCache.class + " instance");
            }
        }
        return self;
    }

    private static String getLocaleBundleName(String bundleName, Locale locale) {
        return bundleName + "_" + locale.getLanguage() + ".properties";
    }

    /**
     * Create a bundle from the resource specified by {@code localeBundleName}
     * and cache it. Return a reference to the newly created bundle.
     *
     * @param localeBundleName the full resource name as returned by
     *                         {@link #getLocaleBundleName}
     * @return the newly created and cached bundle
     */
    private ResourceBundle createBundle(String localeBundleName) {
        if (log.isDebugEnabled()) {
            log.debug("Loading '" + localeBundleName + "'");
        }

        InputStream resourceStream = ClassLoader.getSystemResourceAsStream(localeBundleName);

        if (resourceStream == null) {
            throw new MissingResourceException("No such resource '" + localeBundleName + "'", localeBundleName, "");
        }
        try {
            // The Java 1.6 way is much nicer:
            //ResourceBundle bundle =
            //new PropertyResourceBundle(new InputStreamReader(resourceStream));
            ResourceBundle bundle = new PropertyResourceBundle(new EscapeUTF8Stream(resourceStream));
            cache.put(localeBundleName, bundle);
            return bundle;
        } catch (IOException e) {
            throw new MissingResourceException("Error reading resource '" + localeBundleName + "'",
                    localeBundleName, "");
        }
    }

    /**
     * Get a resource bundle with for a given name and locale.
     * If it is not found the default unlocalized bundle will
     * be tried. If this is not found either a MissingResourceException
     * will be thrown.
     *
     * Both the locale specific and fallback bundles will be cached
     *
     * @param bundleName name of the bundle.
     * @param locale locale for the bundle.
     * @return a bundle ready for use.
     */
    public static synchronized ResourceBundle getBundle(String bundleName, Locale locale) {
        BundleCache self = getInstance();
        String localeBundleName = getLocaleBundleName(bundleName, locale);
        ResourceBundle bundle = self.cache.get(localeBundleName);

        if (bundle != null) {
            if (self.log.isTraceEnabled()) {
                self.log.trace("Found '" + localeBundleName + "' in cache");
            }
            return bundle;
        }

        // We do not have the bundle cached. Create it and cache it
        try {
            return self.createBundle(localeBundleName);
        } catch (MissingResourceException e) {
            self.log.debug(e.getMessage() + ". Falling back to '" + bundleName + "'."); // stack trace left out on purpose
            bundle = self.cache.get(bundleName + ".properties");
            if (bundle != null) {
                return bundle;
            }

            return self.createBundle(bundleName + ".properties");
        }

    }

    /**
     * Return a list of the cached resources
     *
     * @return the list of cached resources
     */
    public Set<String> getResources() {
        return cache.keySet();
    }

    /**
     * Clear all cached bundles.
     */
    public void clear() {
        cache.clear();
    }

}