Index: src/test/com/opensymphony/xwork2/util/TextParseUtilTest.java =================================================================== --- src/test/com/opensymphony/xwork2/util/TextParseUtilTest.java (revision 1542) +++ src/test/com/opensymphony/xwork2/util/TextParseUtilTest.java (working copy) @@ -6,6 +6,7 @@ import com.opensymphony.xwork2.XWorkTestCase; +import java.util.HashMap; import java.util.List; import java.util.HashSet; import java.util.Arrays; @@ -100,5 +101,21 @@ Object s = TextParseUtil.translateVariables('$', "foo: ${}", stack); assertEquals("foo: ", s); } + + public void testTranslateVariablesNoRecursive() { + ValueStack stack = ValueStackFactory.getFactory().createValueStack(); + stack.push(new HashMap() {{ put("foo", "${1+1}"); }}); + Object s = TextParseUtil.translateVariables('$', "foo: ${foo}", stack, String.class, null, 1); + assertEquals("foo: ${1+1}", s); + } + + public void testTranslateVariablesRecursive() { + ValueStack stack = ValueStackFactory.getFactory().createValueStack(); + stack.push(new HashMap() {{ put("foo", "${1+1}"); }}); + + Object s = TextParseUtil.translateVariables('$', "foo: ${foo}", stack, String.class, null, 2); + assertEquals("foo: 2", s); + } + } Index: src/java/com/opensymphony/xwork2/util/TextParseUtil.java =================================================================== --- src/java/com/opensymphony/xwork2/util/TextParseUtil.java (revision 1542) +++ src/java/com/opensymphony/xwork2/util/TextParseUtil.java (working copy) @@ -20,6 +20,8 @@ * @version $Date$ $Id$ */ public class TextParseUtil { + + private static final int MAX_RECURSION = 1; /** * Converts all instances of ${...} in expression to the value returned @@ -95,12 +97,37 @@ * @return Converted object from variable translation. */ public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) { + return translateVariables(open, expression, stack, asType, evaluator, MAX_RECURSION); + } + + /** + * Converted object from variable translation. + * + * @param open + * @param expression + * @param stack + * @param asType + * @param evaluator + * @return Converted object from variable translation. + */ + public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator, int maxLoopCount) { // deal with the "pure" expressions first! //expression = expression.trim(); Object result = expression; - + int loopCount = 1; + int pos = 0; while (true) { - int start = expression.indexOf(open + "{"); + + int start = expression.indexOf(open + "{", pos); + if (start == -1) { + pos = 0; + loopCount++; + start = expression.indexOf(open + "{"); + } + if (loopCount > maxLoopCount) { + // translateVariables prevent infinite loop / expression recursive evaluation + break; + } int length = expression.length(); int x = start + 2; int end; @@ -127,23 +154,29 @@ String left = expression.substring(0, start); String right = expression.substring(end + 1); + String middle = null; if (o != null) { - if (TextUtils.stringSet(left)) { - result = left + o; + middle = o.toString(); + if (!TextUtils.stringSet(left)) { + result = o; } else { - result = o; + result = left + middle; } - + if (TextUtils.stringSet(right)) { result = result + right; } - expression = left + o + right; + expression = left + middle + right; } else { // the variable doesn't exist, so don't display anything result = left + right; expression = left + right; } + pos = (left != null && left.length() > 0 ? left.length() - 1: 0) + + (middle != null && middle.length() > 0 ? middle.length() - 1: 0) + + 1; + pos = Math.max(pos, 1); } else { break; }