From b2431dd4bdc5ede8a2a7b163d039b3e8056c3110 Mon Sep 17 00:00:00 2001 From: Henri Biestro Date: Fri, 17 Nov 2023 20:01:38 -0800 Subject: [PATCH] JEXL-415: fix handling of deferred/immediate characters not followed by curly bracket; --- .../jexl3/internal/TemplateEngine.java | 104 +++++++++--------- .../apache/commons/jexl3/Issues400Test.java | 23 ++++ 2 files changed, 76 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java index 8e15b9d1f..6737f11e8 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java +++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java @@ -838,8 +838,10 @@ TemplateExpression parseExpression(final JexlInfo info, final String expr, final } else { // revert to CONST strb.append(immediateChar); - strb.append(c); state = ParseState.CONST; + // 'unread' the current character + column -= 1; + continue; } break; case DEFERRED0: // # @@ -854,8 +856,10 @@ TemplateExpression parseExpression(final JexlInfo info, final String expr, final } else { // revert to CONST strb.append(deferredChar); - strb.append(c); state = ParseState.CONST; + // 'unread' the current character + column -= 1; + continue; } break; case IMMEDIATE1: // ${... @@ -884,59 +888,57 @@ TemplateExpression parseExpression(final JexlInfo info, final String expr, final break; case DEFERRED1: // #{... // skip inner strings (for '}') - // nested immediate in deferred; need to balance count of '{' & '}' - // closing '}' switch (c) { - case '"': - case '\'': - strb.append(c); - column = StringParser.readString(strb, expr, column + 1, c); - continue; - case '{': - if (expr.charAt(column - 1) == immediateChar) { - inner1 += 1; - strb.deleteCharAt(strb.length() - 1); - nested = true; - } else { - deferred1 += 1; - strb.append(c); - } - continue; - case '}': - // balance nested immediate - if (deferred1 > 0) { - deferred1 -= 1; - strb.append(c); - } else if (inner1 > 0) { - inner1 -= 1; - } else { - // materialize the nested/deferred expr - final String src = strb.toString(); - TemplateExpression dexpr; - if (nested) { - dexpr = new NestedExpression( - expr.substring(inested, column + 1), - jexl.parse(info.at(lineno, column), noscript, src, scope), - null); - } else { - dexpr = new DeferredExpression( - strb.toString(), - jexl.parse(info.at(lineno, column), noscript, src, scope), - null); + case '"': + case '\'': + strb.append(c); + column = StringParser.readString(strb, expr, column + 1, c); + continue; + case '{': + if (expr.charAt(column - 1) == immediateChar) { + inner1 += 1; + strb.deleteCharAt(strb.length() - 1); + nested = true; + } else { + deferred1 += 1; + strb.append(c); + } + continue; + case '}': + // balance nested immediate + if (deferred1 > 0) { + deferred1 -= 1; + strb.append(c); + } else if (inner1 > 0) { + inner1 -= 1; + } else { + // materialize the nested/deferred expr + final String src = strb.toString(); + TemplateExpression dexpr; + if (nested) { + dexpr = new NestedExpression( + expr.substring(inested, column + 1), + jexl.parse(info.at(lineno, column), noscript, src, scope), + null); + } else { + dexpr = new DeferredExpression( + strb.toString(), + jexl.parse(info.at(lineno, column), noscript, src, scope), + null); + } + builder.add(dexpr); + strb.delete(0, Integer.MAX_VALUE); + nested = false; + state = ParseState.CONST; + } + break; + default: + // do buildup expr + column = append(strb, expr, column, c); + break; } - builder.add(dexpr); - strb.delete(0, Integer.MAX_VALUE); - nested = false; - state = ParseState.CONST; - } - break; - default: - // do buildup expr - column = append(strb, expr, column, c); - break; - } break; case ESCAPE: if (c == deferredChar) { diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java b/src/test/java/org/apache/commons/jexl3/Issues400Test.java index af1d565ba..f151fd35f 100644 --- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java +++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java @@ -303,4 +303,27 @@ public void test413d() { Assert.assertTrue(xvar.getMessage().contains("const")); } } + + @Test + public void test415() { + final JexlBuilder builder = new JexlBuilder().features(new JexlFeatures().constCapture(true)); + final JexlEngine jexl = builder.create(); + JexlScript script; + Object result; + script = jexl.createScript("`#${c}`", "c"); + result = script.execute(null, 42); + Assert.assertEquals("#42",result.toString() ); + script = jexl.createScript("`$${c}`", "c"); + result = script.execute(null, 42); + Assert.assertEquals("$42",result.toString() ); + script = jexl.createScript("`$#{c}`", "c"); + result = script.execute(null, 42); + Assert.assertEquals("$42",result.toString() ); + script = jexl.createScript("`##{c}`", "c"); + result = script.execute(null, 42); + Assert.assertEquals("#42",result.toString() ); + script = jexl.createScript("`--##{c}`", "c"); + result = script.execute(null, 42); + Assert.assertEquals("--#42",result.toString() ); + } }