package org.ovirt.engine.ui.frontend; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.MissingResourceException; import org.ovirt.engine.core.compat.StringHelper; import com.google.gwt.i18n.client.ConstantsWithLookup; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; public class ErrorTranslator { private static final String VARIABLE_PATTERN = "\\$\\{\\w*\\}*"; //$NON-NLS-1$ private static final RegExp STARTS_WITH_VARIABLE = RegExp.compile("^" + VARIABLE_PATTERN, "i"); //$NON-NLS-1$ //$NON-NLS-2$ private ConstantsWithLookup errors; public ErrorTranslator() { } public ErrorTranslator(ConstantsWithLookup errors) { this.errors = errors; } /** * Translate errors from error types, error messages contains errors and variables. Variable used in messages, * Variable definition must be in format: $variableName variableValue. Variable usage must be in format * #variableName * * @param errorMsg * messages to be translated * @param changeIfNotFound * If true: if message key is not found in the resource, return a beautified key. If false, returned * unfound key as is. */ public ArrayList<String> translateErrorText(ArrayList<String> errorMsg, Boolean changeIfNotFound) { ArrayList<String> translatedMessages = translateMessages(errorMsg, changeIfNotFound); return resolveMessages(translatedMessages); } /** * Translates and resolves errors from error types. error messages contains errors and variables. Variable used in * messages. Variable definition must be in format: $variableName variableValue. Variable usage must be in format * #variableName Note: Unfound message keys will be beautified! * * @param errorMsg * messages to be translated */ public ArrayList<String> translateErrorText(ArrayList<String> errorMsg) { return translateErrorText(errorMsg, true); } public ArrayList<String> translateMessages(ArrayList<String> errorMsg, Boolean changeIfNotFound) { ArrayList<String> translatedMessages = new ArrayList<>(); if (errorMsg != null && errorMsg.size() > 0) { for (String curError : errorMsg) { translatedMessages.add(translateErrorTextSingle(curError, changeIfNotFound)); } } return translatedMessages; } /** * Translates a single error message. * * @param errorMsg * The message to be translated * @param changeIfNotFound * If true: if message key is not found in the resource, return a beautified key. If false, returned * unfound key as is. */ public String translateErrorTextSingle(String errorMsg, Boolean changeIfNotFound) { String ret = ""; //$NON-NLS-1$ try { if ((errorMsg != null) && (errorMsg.length() > 0)) { String errMsgCopy = errorMsg; // Taking a copy of the error message if (!isDynamicVariable(errorMsg)) { errorMsg = errorMsg.replace('.', '_'); } String errorsString = errors.getString(errorMsg); if (errorsString != null) { ret = errorsString.replace("\n", "<br/>"); //$NON-NLS-1$ //$NON-NLS-2$ } else { if (isDynamicVariable(errorMsg) || !changeIfNotFound) { ret = errorMsg; } else { // The error message is not found in the errors map, revert to original one // without replacement of "." with "_" errorMsg = errMsgCopy; // just a message that doesn't have a value in the resource: String[] splitted = errorMsg.toLowerCase().split("_"); //$NON-NLS-1$ ret = StringHelper.join(" ", splitted); //$NON-NLS-1$ } } } } catch (MissingResourceException e) { ret = errorMsg; } return ret; } /** * Translates a single error message. Note: if message key not found, a beautified message will return! * * @param errorMsg * the message to translate * @return the translated message or a beautifed message key */ public String translateErrorTextSingle(String errorMsg) { return translateErrorTextSingle(errorMsg, true); } /** * Replacing variables ('#...') within translatedMessages with their values ('$...') that are also within * translatedMessages. */ public ArrayList<String> resolveMessages(ArrayList<String> translatedMessages) { ArrayList<String> translatedErrors = new ArrayList<>(); Map<String, LinkedList<String>> variables = new HashMap<>(); for (String currentMessage : translatedMessages) { if (isVariableDeclaration(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, Map<String, LinkedList<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.get(key) == null) { variables.put(key, new LinkedList<String>()); } variables.get(key).add(value); } } private String resolveMessage(String message, Map<String, LinkedList<String>> variables) { String returnValue = message; RegExp regex = RegExp.compile(VARIABLE_PATTERN, "gi"); //$NON-NLS-1$ //$NON-NLS-2$ MatchResult result; while (returnValue.length() > 0) { result = regex.exec(returnValue); if (result == null) { // No more matches break; } String match = result.getGroup(0); String key = match.substring(2, match.length() - 1); if (variables.containsKey(key)) { LinkedList<String> values = variables.get(key); String value = values.size() == 1 ? values.getFirst() : values.size() > 1 ? values.removeFirst() : ""; //$NON-NLS-1$ returnValue = returnValue.replace(match, value); } else { // Variable not found, break the cycle to avoid // infinite loop break; } // Make the next search start from the beginning regex.setLastIndex(0); } return returnValue; } /** * Returns true if the specified strMessage is in the format: "$variable-name variable-value", false otherwise. * * @param strMessage * the string that may be a dynamic variable * @return true if input is dynamic variable, false otherwise. */ private boolean isDynamicVariable(String strMessage) { return strMessage.startsWith("$"); //$NON-NLS-1$ } /** * Returns true if and only if the param starts with $ but is not a variable reference (e.g. is not ${something}) */ boolean isVariableDeclaration(String msg) { boolean startsAsVariable = msg.startsWith("$"); //$NON-NLS-1$ boolean isVariableReference = STARTS_WITH_VARIABLE.test(msg); return startsAsVariable && !isVariableReference; } }