package org.ovirt.engine.core.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.ovirt.engine.core.common.interfaces.ErrorTranslator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ErrorTranslatorImpl implements ErrorTranslator {
private static final long ONE_HOUR = 60 * 60 * 1000L;
private static final Logger log = LoggerFactory.getLogger(ErrorTranslatorImpl.class);
private static final Pattern startsWithVariableDefinitionPattern = Pattern.compile("^\\$[^{}\\s]+ .*");
private List<String> messageSources;
private Locale standardLocale;
private Map<String, String> standardMessages;
private ReapedMap<Locale, Map<String, String>> messagesByLocale;
// Will assume these are property files, not ResxFiles.
public ErrorTranslatorImpl(String... errorFileNames) {
log.info("Start initializing {}", getClass().getSimpleName());
messageSources = asList(errorFileNames);
standardLocale = Locale.getDefault();
standardMessages = retrieveByLocale(standardLocale);
messagesByLocale = new ReapedMap<>(ONE_HOUR, true);
log.info("Finished initializing {}", getClass().getSimpleName());
}
private synchronized Map<String, String> getMessages(Locale locale) {
Map<String, String> messages = null;
if (standardLocale.equals(locale)) {
messages = standardMessages;
} else {
if ((messages = messagesByLocale.get(locale)) == null) {
messages = retrieveByLocale(locale);
messagesByLocale.put(locale, messages);
messagesByLocale.reapable(locale);
}
}
return messages;
}
private Map<String, String> retrieveByLocale(Locale locale) {
Map<String, String> messages = new HashMap<>();
for (String messageSource : messageSources) {
retrieveByLocale(locale, messageSource, messages);
}
return messages;
}
private Map<String, String> retrieveByLocale(Locale locale, String messageSource, Map<String, String> messages) {
try {
ResourceBundle bundle = ResourceBundle.getBundle(messageSource, locale);
for (String key : bundle.keySet()) {
if (!messages.containsKey(key)) {
messages.put(key, bundle.getString(key));
} else {
log.warn("Code '{}' appears more than once in string table.", key);
}
}
} catch (RuntimeException e) {
log.error("File: '{}' could not be loaded: {}", messageSource, e.getMessage());
log.debug("Exception", e);
}
return messages;
}
private List<String> translate(List<String> errorMsg, boolean changeIfNotFound, Locale locale) {
List<String> translatedMessages = doTranslation(errorMsg, changeIfNotFound, locale);
return resolveMessages(translatedMessages);
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#translateErrorText(java.util.List)
*/
public List<String> translateErrorText(List<String> errorMsg) {
return translate(errorMsg, true, Locale.getDefault());
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#translateErrorText(java.util.List)
*/
public List<String> translateErrorText(List<String> errorMsg, Locale locale) {
return translate(errorMsg, true, locale);
}
public final List<String> doTranslation(List<String> errorMsg, boolean changeIfNotFound, Locale locale) {
ArrayList<String> translatedMessages = new ArrayList<>();
if (errorMsg != null && errorMsg.size() > 0) {
for (String curError : errorMsg) {
translatedMessages.add(translate(curError, changeIfNotFound, locale));
}
}
return translatedMessages;
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#isDynamicVariable(java.lang.String )
*/
public final boolean isDynamicVariable(String strMessage) {
return startsWithVariableDefinitionPattern.matcher(strMessage).matches();
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#translateErrorTextSingle(java. lang.String, boolean)
*/
public final String translateErrorTextSingle(String errorMsg, boolean changeIfNotFound) {
return translate(errorMsg, changeIfNotFound, Locale.getDefault());
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#translateErrorTextSingle(java. lang.String, boolean)
*/
public String translateErrorTextSingle(String errorMsg, Locale locale) {
return translate(errorMsg, true, locale);
}
private String translate(String errorMsg, boolean changeIfNotFound, Locale locale) {
String ret = "";
Map<String, String> messages = getMessages(locale);
if (messages != null && messages.containsKey(errorMsg)) {
ret = messages.get(errorMsg);
} else {
if (!(errorMsg == null || errorMsg.isEmpty())) {
if (isDynamicVariable(errorMsg) || !changeIfNotFound) {
ret = errorMsg;
} else {
// just a message that doesn't have a value in the resource:
String[] splitted = errorMsg.toLowerCase().split("[_]", -1);
ret = StringUtils.join(splitted, " ");
}
}
}
return ret;
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#translateErrorTextSingle(java. lang.String)
*/
public final String translateErrorTextSingle(String errorMsg) {
return translateErrorTextSingle(errorMsg, true);
}
/*
* (non-Javadoc)
*
* @see org.ovirt.engine.core.utils.ErrorTranslator#resolveMessages(java.util.List)
*/
public final List<String> resolveMessages(List<String> translatedMessages) {
ArrayList<String> translatedErrors = new ArrayList<>();
HashMap<String, String> variables = new HashMap<>();
for (String currentMessage : translatedMessages) {
if (isDynamicVariable(currentMessage)) {
addVariable(currentMessage, variables);
} else {
translatedErrors.add(currentMessage);
}
}
/**
* Place to global variable adding
*/
ArrayList<String> returnValue = new ArrayList<>();
for (String error : translatedErrors) {
returnValue.add(resolveMessage(error, variables));
}
return returnValue;
}
private void addVariable(String variable, HashMap<String, String> variables) {
int firstSpace = variable.indexOf(' ');
if (firstSpace != -1 && firstSpace < variable.length()) {
String key = variable.substring(1, firstSpace);
String value = variable.substring(firstSpace + 1);
if (!variables.containsKey(key)) {
variables.put(key, value);
}
}
}
private String resolveMessage(String message, HashMap<String, String> variables) {
StrSubstitutor sub = new StrSubstitutor(variables);
return sub.replace(message);
}
private static List<String> asList(String[] names) {
List<String> ret = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
ret.add(trim(names[i]));
}
return ret;
}
private static String trim(String name) {
return name != null && name.endsWith(".properties")
? name.substring(0, name.lastIndexOf(".properties"))
: name;
}
}