blob: ed75748ec95eac4bd623cd46c8bfec5871f3bba8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.openjpa.lib.util;
import java.security.AccessController;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* The Localizer provides convenient access to localized
* strings. It inlcudes built-in support for parameter substitution through
* the use of the {@link MessageFormat} utility.
* Strings are stored in per-package {@link Properties} files.
* The property file for the default locale must be named
* <code>localizer.properties</code>. Additional locales can be supported
* through additional property files using the naming conventions specified
* in the {@link ResourceBundle} class. For example, the german locale
* could be supported through a <code>localizer_de_DE.properties</code> file.
*
* @author Abe White
*/
public class Localizer {
// static cache of package+loc name to localizer mappings
private static final Map<String,Localizer> _localizers = new ConcurrentHashMap<>();
// list of resource providers to delegate to when locating resources
private static final Collection<ResourceBundleProvider> _providers =
new CopyOnWriteArraySet<>
(Arrays.asList(new ResourceBundleProvider[]{
new SimpleResourceBundleProvider(),
new StreamResourceBundleProvider(),
new ZipResourceBundleProvider(), }));
/**
* Return a Localizer instance that will access the properties file
* in the package of the given class using the system default locale.
*
* @see #forPackage(Class,Locale)
*/
public static Localizer forPackage(Class<?> cls) {
return forPackage(cls, null);
}
/**
* Return a Localizer instance that will access the properties file
* in the package of the given class using the given locale.
*
* @param cls the class whose package to check for the localized
* properties file; if null, the system will check for
* a top-level properties file
* @param locale the locale to which strings should be localized; if
* null, the system default will be assumed
*/
public static Localizer forPackage(Class<?> cls, Locale locale) {
if (locale == null)
locale = Locale.getDefault();
int dot = (cls == null) ? -1 : cls.getName().lastIndexOf('.');
String pkg;
String file;
if (dot == -1) {
pkg = "";
file = "localizer";
} else {
pkg = cls.getName().substring(0, dot);
file = pkg + ".localizer";
}
String key = file + locale.toString();
// no locking; ok if bundle created multiple times
// check for cached version
Localizer loc = (Localizer) _localizers.get(key);
if (loc != null)
return loc;
else {
loc = new Localizer(pkg, file, locale,
cls == null ? null:AccessController.doPrivileged(
J2DoPrivHelper.getClassLoaderAction(cls)));
_localizers.put(key, loc);
return loc;
}
}
/**
* Register a resource provider.
*/
public static void addProvider(ResourceBundleProvider provider) {
_providers.add(provider);
}
/**
* Remove a resource provider.
*/
public static boolean removeProvider(ResourceBundleProvider provider) {
return _providers.remove(provider);
}
private String _file;
private String _pkg;
private ResourceBundle _bundle = null;
private Locale _locale;
private ClassLoader _loader;
private Localizer(String pkg, String f, Locale locale, ClassLoader loader) {
_pkg = pkg;
_file = f;
_locale = locale;
_loader = loader;
}
private ResourceBundle getBundle() {
// no locking; it's ok to create multiple bundles
if (_bundle == null) {
// find resource bundle
for (Iterator<ResourceBundleProvider> itr = _providers.iterator();
itr.hasNext() && _bundle == null; ) {
_bundle = itr.next().findResource(_file, _locale, _loader);
}
}
return _bundle;
}
/**
* Return the localized string matching the given key.
*/
public Message get(String key) {
return get(key, null);
}
/**
* Return the localized string matching the given key.
*/
public Message getFatal(String key) {
return getFatal(key, null);
}
/**
* Return the localized string matching the given key. The given
* <code>sub</code> object will be packed into an array and substituted
* into the found string according to the rules of the
* {@link MessageFormat} class.
*
* @see #get(String)
*/
public Message get(String key, Object sub) {
return get(key, new Object[]{ sub });
}
/**
* Return the localized string matching the given key. The given
* <code>sub</code> object will be packed into an array and substituted
* into the found string according to the rules of the
* {@link MessageFormat} class.
*
* @see #getFatal(String)
*/
public Message getFatal(String key, Object sub) {
return getFatal(key, new Object[]{ sub });
}
/**
* Return the localized string for the given key.
*
* @see #get(String,Object)
*/
public Message get(String key, Object sub1, Object sub2) {
return get(key, new Object[]{ sub1, sub2 });
}
/**
* Return the localized string for the given key.
*
* @see #getFatal(String,Object)
*/
public Message getFatal(String key, Object sub1, Object sub2) {
return getFatal(key, new Object[]{ sub1, sub2 });
}
/**
* Return the localized string for the given key.
*
* @see #get(String,Object)
*/
public Message get(String key, Object sub1, Object sub2, Object sub3) {
return get(key, new Object[]{ sub1, sub2, sub3 });
}
/**
* Return the localized string matching the given key. The given
* <code>subs</code> objects will be substituted
* into the found string according to the rules of the
* {@link MessageFormat} class.
*
* @see #get(String)
*/
public Message get(String key, Object[] subs) {
return new Message(_pkg, getBundle(), key, subs, false);
}
/**
* Return the localized string matching the given key. The given
* <code>subs</code> objects will be substituted
* into the found string according to the rules of the
* {@link MessageFormat} class.
*
* @see #getFatal(String)
*/
public Message getFatal(String key, Object[] subs) {
return new Message(_pkg, getBundle(), key, subs, true);
}
/**
* A <code>Message</code> can provide a localized message via the
* {@link #getMessage} method call, and can also provide the original key,
* package, and substitution array that were used to assemble the message.
*/
public static class Message {
private final String _pkg;
private final String _key;
private final Object[] _subs;
private final String _localizedMessage;
private boolean _localizedMessageFound;
private Message(String packageName, ResourceBundle bundle, String key,
Object[] subs, boolean fatal) {
if (bundle == null && fatal)
throw new MissingResourceException(key, key, key);
_pkg = packageName;
_key = key;
_subs = subs;
if (bundle == null) {
_localizedMessage = key;
_localizedMessageFound = false;
} else {
String localized = null;
try {
localized = bundle.getString(key);
_localizedMessageFound = true;
} catch (MissingResourceException mre) {
if (fatal)
throw mre;
_localizedMessageFound = false;
}
_localizedMessage = (localized == null) ? key : localized;
}
}
/**
* The localized message.
*/
public String getMessage() {
if (_localizedMessageFound)
return MessageFormat.format(_localizedMessage, _subs);
else if (_subs == null || _subs.length == 0)
return "localized message key: " + _localizedMessage;
else
return "localized message key: " + _localizedMessage
+ "; substitutions: " + Arrays.asList(_subs).toString();
}
/**
* The unique key for the localized message.
*/
public String getKey() {
return _key;
}
/**
* Substitutions inserted into the message.
*/
public Object[] getSubstitutions() {
return _subs;
}
public String getPackageName() {
return _pkg;
}
@Override
public String toString() {
return getMessage();
}
}
}