package org.ngrinder.infra.config;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.ngrinder.common.util.EncodingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Component;
import com.google.common.collect.Maps;
/**
* Custom user defined message source handler. User can defines its own message translations in
* ${NGRINDER_HOME}/messages/messages_{langcode}.properties.
*
* @author JunHo Yoon
* @since 3.1
*/
@Component("userMessageSource")
public class UserMessageSource extends AbstractMessageSource {
private static final Logger LOGGER = LoggerFactory.getLogger(UserMessageSource.class);
@Autowired
private Config config;
// It's safe to use hash map in multi thread here. because it's read only.
private Map<LocaleAndCode, MessageFormat> langMessageMap = Maps.newHashMap();
/**
* Message key holder with local and code.
*
* @author JunHo Yoon
* @since 3.1
*/
static class LocaleAndCode {
/**
* Constructor.
*
* @param locale locale
* @param code code
*/
public LocaleAndCode(String locale, String code) {
this.locale = locale;
this.code = code;
}
private String locale;
private String code;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result + ((locale == null) ? 0 : locale.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
}
/**
* Refresh i18n messages.
* @throws IOException IO exception
*/
public void refresh() throws IOException {
init();
}
/**
* Initialize.
*/
@PostConstruct
public void init() {
File messagesDirectory = config.getHome().getMessagesDirectory();
if (messagesDirectory.exists()) {
for (String each : Locale.getISOLanguages()) {
File file = new File(messagesDirectory, "messages_" + each + ".properties");
if (file.exists()) {
try {
byte[] propByte = FileUtils.readFileToByteArray(file);
String propString = EncodingUtils.getAutoDecodedString(propByte, "UTF-8");
Properties prop = new Properties();
prop.load(new StringReader(propString));
for (Map.Entry<Object, Object> eachEntry : prop.entrySet()) {
langMessageMap.put(new LocaleAndCode(each, (String) eachEntry.getKey()), new MessageFormat(
(String) eachEntry.getValue()));
}
} catch (Exception e) {
LOGGER.error("Error while loading {}", file.getAbsolutePath(), e);
}
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.context.support.AbstractMessageSource#resolveCode(java.lang.String,
* java.util.Locale)
*/
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
MessageFormat resolved = langMessageMap.get(new LocaleAndCode(locale.getLanguage(), code));
return resolved == null ? langMessageMap.get(new LocaleAndCode("en", code)) : resolved;
}
}