com.jamonapi.utils.LocaleContext Maven / Gradle / Ivy
package com.jamonapi.utils;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
/**
*
* Provides a context for localized parameters, mainly formatters, in thread local scope.
* Note normally the Formatting classes are not thread safe.
* This class uses ThreadLocal to return a unique object to each thread, so the getFloatingPointFormatter
* returns the same object in one thread, and getIntegerFormatter returns another one in the same thread.
* By using only LocaleContext to get Format objects instead of constructing Format objects
* otherwise, thread-safety is ensured. This class is tuned for performance: constructing a Format
* object is rather expensive, this class only creates them when really needed.
*
*/
public final class LocaleContext {
/** the thread local storage for the locale specific formatters */
private static ThreadLocalFormatterStorage formatterStorage = new ThreadLocalFormatterStorage();
/** @return the thread local storage for the fixed-point number formatting specific for the locale */
public static DecimalFormat getFloatingPointFormatter() {
return getFormatters().getFloatingPointFormatter();
}
/** @return the thread local storage for the integral number formatting specific for the locale */
public static DecimalFormat getIntegerFormatter() {
return getFormatters().getIntegerFormatter();
}
/** @return the thread local storage for the percent number formatting specific for the locale */
public static DecimalFormat getPercentFormatter() {
return getFormatters().getPercentFormatter();
}
/** @return the thread local storage for the currency formatting specific for the locale */
public static DecimalFormat getCurrencyFormatter() {
return getFormatters().getCurrencyFormatter();
}
/**
* @return the thread local storage for the number formatting decimal grouping separator
* specific for the locale
*/
public static char getDecimalGroupSeparator() {
return getFormatters().getDecimalGroupSeparator();
}
/** @return the locale specific date time formatter */
public static DateFormat getDateFormatter() {
return getFormatters().getDateFormatter();
}
/**
* Sets the locale to apply for formatting.
*
* @param locale the locale to apply for formatting.
*/
public static void setLocale(Locale locale) {
getFormatters().setLocale(locale);
}
/**
* Reset thread local variables. For example this can be called when an application is 'undeployed'.
* This was done to fix a memory leak in tomcat that happened when the app context was destroyed.
* The context couldn't be properly destroyed as objects were still in the threadlocal map.
*/
public static void reset() {
formatterStorage = new ThreadLocalFormatterStorage();
}
/**
* @return Returns the thread associated, locale specific formatters
*/
private static Formatters getFormatters() {
return (Formatters) formatterStorage.get();
}
/** Inner class for storage of thread-associated formatters */
private static final class ThreadLocalFormatterStorage extends ThreadLocal {
/**
* @return the current thread's initial value for this thread-local variable. This method
* will be invoked at most once per accessing thread for each thread-local.
*/
@Override
protected Object initialValue() {
return new Formatters();
}
}
/** Inner class of thread-associated formatters */
private static class Formatters {
private Locale locale;
private DecimalFormat floatingPointFormatter;
private DecimalFormat integerFormatter;
/** the number formatting decimal grouping separator */
private char decimalSeparator = 0;
private DateFormat dateFormatter;
private DecimalFormat percentFormatter;
private DecimalFormat currencyFormatter;
void setLocale(Locale locale) {
this.locale = locale;
// now all formatters need to re-created when needed, to apply the new locale
floatingPointFormatter = null;
integerFormatter = null;
decimalSeparator = 0;
dateFormatter = null;
percentFormatter = null;
currencyFormatter = null;
}
Locale getLocale() {
if (locale == null) { // if no locale specified from client
locale = Locale.getDefault();
}
return locale;
}
DecimalFormat getFloatingPointFormatter() {
if (floatingPointFormatter == null) {
floatingPointFormatter = (DecimalFormat) NumberFormat.getNumberInstance(getLocale());
floatingPointFormatter.applyPattern("#,###.#");
}
return floatingPointFormatter;
}
DecimalFormat getIntegerFormatter() {
if (integerFormatter == null) {
integerFormatter = (DecimalFormat) NumberFormat.getNumberInstance(getLocale());
integerFormatter.applyPattern("#,###");
}
return integerFormatter;
}
DecimalFormat getPercentFormatter() {
if (percentFormatter == null) {
percentFormatter = (DecimalFormat) NumberFormat.getPercentInstance(getLocale());
}
return percentFormatter;
}
DecimalFormat getCurrencyFormatter() {
if (currencyFormatter == null) {
currencyFormatter = (DecimalFormat) NumberFormat.getCurrencyInstance(getLocale());
}
return currencyFormatter;
}
char getDecimalGroupSeparator() {
if (decimalSeparator == 0) {
DecimalFormatSymbols symbols = new DecimalFormatSymbols(getLocale());
decimalSeparator = (new Character(symbols.getGroupingSeparator())).charValue();
}
return decimalSeparator;
}
DateFormat getDateFormatter() {
if (dateFormatter == null) {
dateFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.DEFAULT, getLocale());
}
return dateFormatter;
}
}
/** contructs a new LocaleContext, for private use only */
private LocaleContext() {
}
}