package org.marketcetera.util.log; import java.util.EmptyStackException; import java.util.Locale; import java.util.Stack; import java.util.concurrent.Callable; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.misc.CollectionUtils; /** * Manages the active locale. * * <p>Locale management relies on the concept of <i>scopes</i>. The * search for the currently active locale, effected by {@link * #getLocale()}, proceeds from the top-most to the bottom-most scope * until a non-null locale is identified.</p> * * <p>At the lowest scope resides the system default locale, which * cannot be altered via this class. Above it is the process-wide * locale (more precisely, specific to the caller's classloader), * managed via {@link #setProcessLocale(Locale)} and {@link * #getProcessLocale()}. Above it is a thread-specific stack of * locales that mirrors the thread's call stack. This locale stack is * manipulated indirectly, without direct access to stack management * operations. Locales are pushed and popped off the stack as code * blocks are executed via {@link #runWithLocale(Runnable,Locale)} or * {@link #runWithLocale(Callable,Locale)}. Or, the top of the stack * can be directly altered via {@link #setThreadLocale(Locale)} (you * can assume the stack has a top, and therefore use this method, even * if there was no explicit prior call to execute a code block).</p> * * <p>A null locale may be supplied as an argument, or be returned as * the result, by several methods as noted. Its intended semantics are * <i>skip me</i>: a null locale at a scope allows the locale at the * scope below (or, if null as well, the locale below it and so on) to * show through as the active locale.</p> * * @author tlerios@marketcetera.com * @since 0.6.0 * @version $Id: ActiveLocale.java 16154 2012-07-14 16:34:05Z colin $ */ /* $License$ */ @ClassVersion("$Id: ActiveLocale.java 16154 2012-07-14 16:34:05Z colin $") public class ActiveLocale { // CLASS DATA. private static InheritableThreadLocal<Stack<Locale>> sThreadStack= new InheritableThreadLocal<Stack<Locale>>() { @Override protected Stack<Locale> initialValue() { Stack<Locale> stack=new Stack<Locale>(); stack.push(null); return stack; } @Override protected Stack<Locale> childValue (Stack<Locale> parentStack) { Stack<Locale> stack=new Stack<Locale>(); stack.push(CollectionUtils.getLastNonNull(parentStack)); return stack; } }; private static Locale sProcessLocale; // CLASS METHODS. /** * Clears all management settings for the calling thread. This * method is intended for testing purposes only. */ static void clear() { Stack<Locale> stack=new Stack<Locale>(); stack.push(null); sThreadStack.set(stack); setProcessLocale(null); } /** * Returns the caller's active locale. * * @return The locale. */ public static Locale getLocale() { Locale locale=CollectionUtils.getLastNonNull(sThreadStack.get()); if (locale!=null) { return locale; } if (getProcessLocale()!=null) { return getProcessLocale(); } return Locale.getDefault(); } /** * Sets the process-specific (more precisely, specific to the * caller's classloader) locale to the given one. * * @param locale The locale. It may be null. */ public static void setProcessLocale (Locale locale) { sProcessLocale=locale; } /** * Returns the process-specific (more precisely, specific to the * caller's classloader) locale. * * @return The locale. It may be null. */ public static Locale getProcessLocale() { return sProcessLocale; } /** * Sets the locale at the top of the thread-specific (that is, * specific to the caller's thread) locale stack to the given * locale. * * @param locale The locale. It may be null. */ public static void setThreadLocale (Locale locale) { Stack<Locale> stack=sThreadStack.get(); stack.pop(); stack.push(locale); } /** * Pushes the given locale onto the thread-specific (that is, * specific to the caller's thread) locale stack. This call must * be paired up with a call to {@link #popLocale()}. * * @param locale The locale. It may be null. */ public static void pushLocale (Locale locale) { sThreadStack.get().push(locale); } /** * Pop the locale at the top of the thread-specific (that is, * specific to the caller's thread) locale stack. This call must * be paired up with a call to {@link #pushLocale(Locale)}. * * @throws EmptyStackException Thrown if the locale stack is * empty. */ public static void popLocale() { sThreadStack.get().pop(); } /** * Initiates execution of the given runnable within the context of * the given locale as the active one. When execution ends, the * previously active locale is restored. * * @param runnable The runnable. * @param locale The locale. It may be null. */ public static void runWithLocale (Runnable runnable, Locale locale) { pushLocale(locale); try { runnable.run(); } finally { popLocale(); } } /** * Initiates execution of the given callable within the context of * the given locale as the active one. When execution ends, the * previously active locale is restored, and the callable's return * value is returned. * * @param callable The callable. * @param locale The locale. It may be null. * * @return The callable's return value. * * @throws Exception Propagated from the callable's invocation. */ public static <V> V runWithLocale (Callable<V> callable, Locale locale) throws Exception { pushLocale(locale); try { return callable.call(); } finally { popLocale(); } } // CONSTRUCTOR. /** * Constructor. It is private so that no instances can be created. */ private ActiveLocale() {} }