package edu.ualberta.med.biobank.i18n; import java.util.regex.Matcher; import java.util.regex.Pattern; import ognl.Ognl; import ognl.OgnlException; /** * Like {@link java.text.MessageFormat}, but replaces OGNL expressions with * their evaluation using some context. * * @author Jonathan Ferland */ public class OgnlMessageFormatter { private static final Pattern VARIABLE_START_PATTERN = Pattern .compile("\\$\\{"); /** * Enables the OGNL evaluation of any substring of the format * <code>${ognl_expression}</code>, within a given message * {@link String}. Format the message by replacing OGNL expressions with * their evaluated result. * * @param message may contain OGNL expressions for evaluation * @param root object providing the context of the OGNL evaluation * * @return the formatted message */ public static String format(String message, Object root) { String interpolatedMessage = message; Matcher matcher = VARIABLE_START_PATTERN.matcher(message); while (matcher.find()) { int curlyBraceOpenings = 1; boolean inDoubleQuotes = false; boolean inSingleQuotes = false; int lastIndex = matcher.end(); do { char current = message.charAt(lastIndex); if (current == '\'') { if (!inDoubleQuotes && !isEscaped(message, lastIndex)) { inSingleQuotes = !inSingleQuotes; } } else if (current == '"') { if (!inSingleQuotes && !isEscaped(message, lastIndex)) { inDoubleQuotes = !inDoubleQuotes; } } else if (!inDoubleQuotes && !inSingleQuotes) { if (current == '{') { curlyBraceOpenings++; } else if (current == '}') { curlyBraceOpenings--; } } lastIndex++; } while (curlyBraceOpenings > 0 && lastIndex < message.length()); // The validated value expression seems correct if (curlyBraceOpenings == 0) { String variable = message.substring(matcher.start(), lastIndex); String ognlExpression = extractContents(variable); String evaluation = evaluateOgnl(ognlExpression, root); String escapedVariable = Pattern.quote(variable); String escapedEvaluation = Matcher.quoteReplacement(evaluation); interpolatedMessage = interpolatedMessage.replaceFirst( escapedVariable, escapedEvaluation); } } return interpolatedMessage; } /** * * @param variable a string matching <code>${contents}</code> * @return contents */ private static String extractContents(String variable) { int start = 2; int end = variable.length() - 1; if (end < start) { throw new IndexOutOfBoundsException(""); } return variable.substring(start, end); } /** * Return true if the char at the given index is preceded by a backslash in * the containing String. * * @param string the containing string * @param charIndex the index of the character * * @return true if the given character is escaped, otherwise false */ private static boolean isEscaped(String string, int charIndex) { if (charIndex < 0 || charIndex > string.length()) { throw new IndexOutOfBoundsException( "Index must be between 0 and string.length() - 1."); } return charIndex > 0 && string.charAt(charIndex - 1) == '\\'; } private static String evaluateOgnl(String expr, Object root) { String result = null; try { Object value = Ognl.getValue(expr, root); result = value.toString(); } catch (OgnlException e) { // TODO: something better? throw new RuntimeException(e); } return result; } }