Java tutorial
/* * Copyright 2005,2006 WSO2, Inc. http://www.wso2.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wso2.carbon.utils.i18n; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * <p>Wrapper class for resource bundles. Property files are used to store * resource strings, which are the only types of resources available. * Property files can inherit properties from other files so that * a base property file can be used and a small number of properties * can be over-ridden by another property file. For example you may * create an english version of a resource file named "resource.properties". * You then decide that the British English version of all of the properties * except one are the same, so there is no need to redefine all of the * properties in "resource_en_GB", just the one that is different.</p> * <p>The basename is the name of the property file without the ".properties" * extension.</p> * <p>Properties will be cached for performance.<p> * <p>Property values stored in the property files can also contain dynamic * variables. Any dynamic variable defined in PropertiesUtil.getVariableValue() * can be used (such as {date}), as well as arguments in the form {0}, {1}, etc. * Argument values are specified in the various overloaded getString() methods.</p> */ public class ProjectResourceBundle extends ResourceBundle { private static Log log = LogFactory.getLog(ProjectResourceBundle.class.getName()); // The static cache of ResourceBundles. // The key is the 'basename + locale + default locale' // The element is a ResourceBundle object private static final Hashtable bundleCache = new Hashtable(); private static final Locale defaultLocale = Locale.getDefault(); private final ResourceBundle resourceBundle; private final String resourceName; protected Object handleGetObject(String key) { if (log.isDebugEnabled()) { log.debug(this.toString() + "::handleGetObject(" + key + ")"); } Object obj; try { obj = resourceBundle.getObject(key); } catch (MissingResourceException e) { /* catch missing resource, ignore, & return null * if this method doesn't return null, then parents * are not searched */ obj = null; } return obj; } public Enumeration getKeys() { Enumeration myKeys = resourceBundle.getKeys(); if (parent == null) { return myKeys; } else { final HashSet set = new HashSet(); while (myKeys.hasMoreElements()) { set.add(myKeys.nextElement()); } Enumeration pKeys = parent.getKeys(); while (pKeys.hasMoreElements()) { set.add(pKeys.nextElement()); } return new Enumeration() { private Iterator it = set.iterator(); public boolean hasMoreElements() { return it.hasNext(); } public Object nextElement() { return it.next(); } }; } } /** * Construct a new ProjectResourceBundle * * @param projectName The name of the project to which the class belongs. * It must be a proper prefix of the caller's package. * @param packageName The package name to further construct * the basename. * @param resourceName The name of the resource without the * ".properties" extension * @throws MissingResourceException if projectName is not a prefix of * the caller's package name, or if the resource could not be * found/loaded. */ public static ProjectResourceBundle getBundle(String projectName, String packageName, String resourceName) { return getBundle(projectName, packageName, resourceName, null, null, null); } /** * Construct a new ProjectResourceBundle * * @param projectName The name of the project to which the class belongs. * It must be a proper prefix of the caller's package. * @param caller The calling class. * @param resourceName The name of the resource without the * ".properties" extension * @throws MissingResourceException if projectName is not a prefix of * the caller's package name, or if the resource could not be * found/loaded. */ public static ProjectResourceBundle getBundle(String projectName, Class caller, String resourceName, Locale locale) { return getBundle(projectName, caller, resourceName, locale, null); } /** * Construct a new ProjectResourceBundle * * @param projectName The name of the project to which the class belongs. * It must be a proper prefix of the caller's package. * @param packageName The package name to construct base name. * @param resourceName The name of the resource without the * ".properties" extension * @param locale The locale * @throws MissingResourceException if projectName is not a prefix of * the caller's package name, or if the resource could not be * found/loaded. */ public static ProjectResourceBundle getBundle(String projectName, String packageName, String resourceName, Locale locale, ClassLoader loader) { return getBundle(projectName, packageName, resourceName, locale, loader, null); } /** * Construct a new ProjectResourceBundle * * @param projectName The name of the project to which the class belongs. * It must be a proper prefix of the caller's package. * @param caller The calling class. * This is used to get the package name to further construct * the basename as well as to get the proper ClassLoader. * @param resourceName The name of the resource without the * ".properties" extension * @param locale The locale * @param extendsBundle If non-null, then this ExtendMessages will * default to extendsBundle. * @throws MissingResourceException if projectName is not a prefix of * the caller's package name, or if the resource could not be * found/loaded. */ public static ProjectResourceBundle getBundle(String projectName, Class caller, String resourceName, Locale locale, ResourceBundle extendsBundle) { return getBundle(projectName, getPackage(caller.getClass().getName()), resourceName, locale, caller.getClass().getClassLoader(), extendsBundle); } /** * Construct a new ProjectResourceBundle * * @param projectName The name of the project to which the class belongs. * It must be a proper prefix of the caller's package. * @param packageName The package name to further construct * the basename. * @param resourceName The name of the resource without the * ".properties" extension * @param locale The locale * @param extendsBundle If non-null, then this ExtendMessages will * default to extendsBundle. * @throws MissingResourceException if projectName is not a prefix of * the caller's package name, or if the resource could not be * found/loaded. */ public static ProjectResourceBundle getBundle(String projectName, String packageName, String resourceName, Locale locale, ClassLoader loader, ResourceBundle extendsBundle) { if (log.isDebugEnabled()) { log.debug("getBundle(" + projectName + "," + packageName + "," + resourceName + "," + String.valueOf(locale) + ",...)"); } Context context = new Context(); context.setLocale(locale); context.setLoader(loader); context.setProjectName(projectName); context.setResourceName(resourceName); context.setParentBundle(extendsBundle); packageName = context.validate(packageName); ProjectResourceBundle bundle = null; try { bundle = getBundle(context, packageName); } catch (RuntimeException e) { log.debug("Exception: ", e); throw e; } if (bundle == null) { throw new MissingResourceException("Cannot find resource '" + packageName + '.' + resourceName + "'", resourceName, ""); } return bundle; } /** * get bundle... * - check cache * - try up hierarchy * - if at top of hierarchy, use (link to) context.getParentBundle() */ private static synchronized ProjectResourceBundle getBundle(Context context, String packageName) { String cacheKey = context.getCacheKey(packageName); ProjectResourceBundle prb = (ProjectResourceBundle) bundleCache.get(cacheKey); if (prb == null) { String name = packageName + '.' + context.getResourceName(); ResourceBundle rb = context.loadBundle(packageName); ResourceBundle parent = context.getParentBundle(packageName); if (rb != null) { prb = new ProjectResourceBundle(name, rb); prb.setParent(parent); if (log.isDebugEnabled()) { log.debug("Created " + prb + ", linked to parent " + String.valueOf(parent)); } } else { if (parent != null) { if (parent instanceof ProjectResourceBundle) { prb = (ProjectResourceBundle) parent; } else { prb = new ProjectResourceBundle(name, parent); } if (log.isDebugEnabled()) { log.debug("Root package not found, cross link to " + parent); } } } if (prb != null) { // Cache the resource bundleCache.put(cacheKey, prb); } } return prb; } private static String getPackage(String name) { return name.substring(0, name.lastIndexOf('.')).intern(); } /** * Construct a new ProjectResourceBundle */ private ProjectResourceBundle(String name, ResourceBundle bundle) { this.resourceBundle = bundle; this.resourceName = name; } public String getResourceName() { return resourceName; } /** * Clears the internal cache */ public static void clearBundleCache() { bundleCache.clear(); } public String toString() { return resourceName; } private static class Context { private Locale _locale; private ClassLoader _loader; private String _projectName; private String _resourceName; private ResourceBundle _parent; void setLocale(Locale l) { /* 1. Docs indicate that if locale is not specified, * then the default local is used in it's place. * 2. A null value for locale is invalid. * * Therefore, default... */ _locale = (l == null) ? defaultLocale : l; } void setLoader(ClassLoader l) { _loader = (l != null) ? l : this.getClass().getClassLoader(); // START FIX: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16868 if (_loader == null) { _loader = ClassLoader.getSystemClassLoader(); } // END FIX: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16868 } void setProjectName(String name) { _projectName = name.intern(); } void setResourceName(String name) { _resourceName = name.intern(); } void setParentBundle(ResourceBundle b) { _parent = b; } Locale getLocale() { return _locale; } ClassLoader getLoader() { return _loader; } String getProjectName() { return _projectName; } String getResourceName() { return _resourceName; } ResourceBundle getParentBundle() { return _parent; } String getCacheKey(String packageName) { String loaderName = (_loader == null) ? "" : (":" + _loader.hashCode()); return packageName + "." + _resourceName + ":" + _locale + ":" + defaultLocale + loaderName; } ResourceBundle loadBundle(String packageName) { try { return ResourceBundle.getBundle(packageName + '.' + _resourceName, _locale, _loader); } catch (MissingResourceException e) { // Deliberately surpressing print stack.. just the string for info. log.debug("loadBundle: Ignoring MissingResourceException: " + e.getMessage()); } return null; } ResourceBundle getParentBundle(String packageName) { ResourceBundle p; if (!packageName.equals(_projectName)) { p = getBundle(this, getPackage(packageName)); } else { p = _parent; _parent = null; } return p; } String validate(String packageName) { if (_projectName == null || _projectName.length() == 0) { log.debug("Project name not specified"); throw new MissingResourceException("Project name not specified", "", ""); } if (packageName == null || packageName.length() == 0) { log.debug("Package name not specified"); throw new MissingResourceException("Package not specified", packageName, ""); } packageName = packageName.intern(); /* Ensure that project is a proper prefix of class. * Terminate project name with '.' to ensure proper match. */ if (!packageName.equals(_projectName) && !packageName.startsWith(_projectName + '.')) { log.debug("Project not a prefix of Package"); throw new MissingResourceException( "Project '" + _projectName + "' must be a prefix of Package '" + packageName + "'", packageName + '.' + _resourceName, ""); } return packageName; } } }