package io.micronaut.context.i18n;
import io.micronaut.context.AbstractMessageSource;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ResourceBundleMessageSource extends AbstractMessageSource {
private static final Logger LOG = LoggerFactory.getLogger(ResourceBundleMessageSource.class);
private final String baseName;
private final Map<MessageKey, Optional<String>> messageCache =
buildMessageCache();
private final Map<MessageKey, Optional<ResourceBundle>> bundleCache =
buildBundleCache();
private final @Nullable ResourceBundle defaultBundle;
public ResourceBundleMessageSource(@NonNull String baseName) {
this(baseName, null);
}
public ResourceBundleMessageSource(@NonNull String baseName, @Nullable Locale defaultLocale) {
ArgumentUtils.requireNonNull("baseName", baseName);
this.baseName = baseName;
ResourceBundle defaultBundle;
try {
if (defaultLocale != null) {
defaultBundle = ResourceBundle.getBundle(baseName, defaultLocale, getClassLoader());
} else {
defaultBundle = ResourceBundle.getBundle(baseName);
}
} catch (MissingResourceException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("No default bundle (locale: " + defaultLocale + ") found for base name " + baseName);
}
defaultBundle = null;
}
this.defaultBundle = defaultBundle;
}
@NonNull
@Override
public Optional<String> getRawMessage(@NonNull String code, @NonNull MessageContext context) {
final Locale locale = defaultBundle != null ? context.getLocale(defaultBundle.getLocale()) : context.getLocale();
MessageKey messageKey = new MessageKey(locale, code);
Optional<String> opt = messageCache.get(messageKey);
if (opt == null) {
try {
final Optional<ResourceBundle> bundle = resolveBundle(locale);
if (bundle.isPresent()) {
return bundle.map(b -> b.getString(code));
} else {
return resolveDefault(code);
}
} catch (MissingResourceException e) {
opt = resolveDefault(code);
}
messageCache.put(messageKey, opt);
}
return opt;
}
protected ClassLoader getClassLoader() {
return getClass().getClassLoader();
}
@NonNull
protected Map<MessageKey, Optional<String>> buildMessageCache() {
return new ConcurrentLinkedHashMap.Builder<MessageKey, Optional<String>>()
.maximumWeightedCapacity(100)
.build();
}
@NonNull
protected Map<MessageKey, Optional<ResourceBundle>> buildBundleCache() {
return new ConcurrentHashMap<>(18);
}
@NonNull
private Optional<String> resolveDefault(@NonNull String code) {
Optional<String> opt;
if (defaultBundle != null) {
try {
opt = Optional.of(defaultBundle.getString(code));
} catch (MissingResourceException e) {
opt = Optional.empty();
}
} else {
opt = Optional.empty();
}
return opt;
}
private Optional<ResourceBundle> resolveBundle(Locale locale) {
MessageKey key = new MessageKey(locale, baseName);
final Optional<ResourceBundle> resourceBundle = bundleCache.get(key);
if (resourceBundle != null) {
return resourceBundle;
} else {
Optional<ResourceBundle> opt;
try {
opt = Optional.of(ResourceBundle.getBundle(baseName, locale, getClassLoader()));
} catch (MissingResourceException e) {
opt = Optional.empty();
}
bundleCache.put(key, opt);
return opt;
}
}
}