/*
* Copyright 2015-present Facebook, Inc.
*
* 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 com.facebook.buck.i18n;
import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
import javax.annotation.concurrent.ThreadSafe;
/** Thread-safe and i18n-safe wrapper for {@link NumberFormat} (which is not thread-safe). */
@ThreadSafe
public class NumberFormatter {
// Instead of a ThreadLocal<NumberFormat> (which leaks memory),
// we'll use a LoadingCache of <thread+locale: numberformat> pairs
// which evicts itself as necessary.
private final LoadingCache<NumberFormatterCacheKey, NumberFormat> numberFormatCache;
/**
* Given a function which creates {@link NumberFormat} objects for a {@link Locale}, returns a
* wrapper providing thread-safe access to the formatter for that locale.
*/
public NumberFormatter(final Function<Locale, NumberFormat> numberFormatCreator) {
numberFormatCache =
CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<NumberFormatterCacheKey, NumberFormat>() {
@Override
public NumberFormat load(NumberFormatterCacheKey key) {
return numberFormatCreator.apply(key.getLocale());
}
});
}
private NumberFormat getFormatter(Locale locale) {
return numberFormatCache.getUnchecked(
NumberFormatterCacheKey.of(Thread.currentThread().getId(), locale));
}
/** @see NumberFormat#format(double) */
public String format(Locale locale, double number) {
return getFormatter(locale).format(number);
}
/** @see NumberFormat#format(double, StringBuffer, FieldPosition) */
public StringBuffer format(
Locale locale, double number, StringBuffer toAppendTo, FieldPosition pos) {
return getFormatter(locale).format(number, toAppendTo, pos);
}
/** @see NumberFormat#format(long) */
public String format(Locale locale, long number) {
return getFormatter(locale).format(number);
}
/** @see NumberFormat#format(Object) */
public String format(Locale locale, Object object) {
return getFormatter(locale).format(object);
}
/** @see NumberFormat#format(Object, StringBuffer, FieldPosition) */
public StringBuffer format(
Locale locale, Object object, StringBuffer toAppendTo, FieldPosition pos) {
return getFormatter(locale).format(object, toAppendTo, pos);
}
/** @see NumberFormat#parse(String) */
public Number parse(Locale locale, String source) throws ParseException {
return getFormatter(locale).parse(source);
}
/** @see NumberFormat#parse(String, ParsePosition) */
public Number parse(Locale locale, String source, ParsePosition pos) {
return getFormatter(locale).parse(source, pos);
}
/** @see NumberFormat#parseObject(String, ParsePosition) */
public Object parseObject(Locale locale, String source, ParsePosition pos) {
return getFormatter(locale).parseObject(source, pos);
}
}