| /* |
| * 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(); |
| } |
| } |
| } |