diff --git a/expression/src/main/java/io/smallrye/common/expression/Expression.java b/expression/src/main/java/io/smallrye/common/expression/Expression.java
index 555a04d..a42554c 100644
--- a/expression/src/main/java/io/smallrye/common/expression/Expression.java
+++ b/expression/src/main/java/io/smallrye/common/expression/Expression.java
@@ -195,6 +195,10 @@ static final class Itr {
this.str = str;
}
+ boolean hasPrev() {
+ return idx > 0;
+ }
+
boolean hasNext() {
return idx < str.length();
}
@@ -258,6 +262,25 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
// treat as plain content
continue;
}
+ if (flags.contains(Flag.STRICT_ESCAPES)) {
+ if (!itr.hasNext()) {
+ continue;
+ } else if (itr.peekNext() == '$') {
+ list.add(new LiteralNode(itr.getStr(), start, itr.getNextIdx()));
+ start = itr.getNextIdx();
+ continue;
+ } else if (itr.peekNext() != '{') {
+ continue;
+ } else if (itr.hasPrev()) {
+ itr.prev();
+ if (itr.hasPrev() && itr.peekPrev() == '$') {
+ itr.next();
+ start = itr.getNextIdx();
+ continue;
+ }
+ itr.next();
+ }
+ }
// check to see if it's a dangling $
if (!itr.hasNext()) {
if (!flags.contains(Flag.LENIENT_SYNTAX)) {
@@ -614,6 +637,21 @@ private static Node parseString(Itr itr, final boolean allowExpr, final boolean
start = itr.getNextIdx();
continue;
}
+ } else if (flags.contains(Flag.STRICT_ESCAPES)) {
+ int escape = itr.getNextIdx();
+ if (itr.hasNext() && itr.peekNext() == '$') {
+ itr.next();
+ if (itr.hasNext() && itr.peekNext() == '{') {
+ list.add(new LiteralNode(itr.getStr(), start, escape - 1));
+ list.add(new LiteralNode(itr.getStr(), escape, itr.getNextIdx()));
+ start = itr.getNextIdx();
+ } else if (itr.hasNext() && itr.peekNext() == '$') {
+ list.add(new LiteralNode(itr.getStr(), start, escape));
+ start = itr.getPrevIdx();
+ itr.prev();
+ }
+ }
+ continue;
}
// TP 42
// otherwise, just...
@@ -694,6 +732,11 @@ public enum Flag {
* character.
*/
ESCAPES,
+ /**
+ * Escaping $
with $$
or /$
only applies when {
follows
+ * the initial escaped $
.
+ */
+ STRICT_ESCAPES,
/**
* Treat expressions containing a double-colon delimiter as special, encoding the entire content into the key.
*/
diff --git a/expression/src/test/java/io/smallrye/common/expression/ExpressionTestCase.java b/expression/src/test/java/io/smallrye/common/expression/ExpressionTestCase.java
index 53046ca..0a32038 100644
--- a/expression/src/test/java/io/smallrye/common/expression/ExpressionTestCase.java
+++ b/expression/src/test/java/io/smallrye/common/expression/ExpressionTestCase.java
@@ -693,4 +693,98 @@ void expressions() {
b.append(c.getExpandedDefault());
}));
}
+
+ @Test
+ void strictEscapes() {
+ assertEquals("$", Expression.compile("$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$$", Expression.compile("$$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("\\$", Expression.compile("\\$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("\\$$", Expression.compile("\\$$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$$foo", Expression.compile("$$foo", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$", Expression.compile("foo$$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$$foo", Expression.compile("$$foo", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$bar", Expression.compile("foo$$bar", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("${foo}", Expression.compile("$${foo}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$${foo}", Expression.compile("$$${foo}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$${foo}$", Expression.compile("$$${foo}$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$${foo}$$", Expression.compile("$$${foo}$$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo${bar}", Expression.compile("foo$${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$${bar}", Expression.compile("foo$$${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$$${bar}", Expression.compile("foo$$$$${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$$${bar}$$$baz", Expression.compile("foo$$$$${bar}$$$baz", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$$$", Expression.compile("foo$$$$", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("${foo:bar}", Expression.compile("$${foo:bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("$${foo:bar}", Expression.compile("$$${foo:bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("${foo:}", Expression.compile("$${foo:${bar}}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("${foo:${bar}}", Expression.compile("$${foo:$${bar}}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+
+ assertEquals("", Expression.compile("${foo}", STRICT_ESCAPES).evaluate((c, b) -> {
+ assertEquals("foo", c.getKey());
+ }));
+ assertEquals("", Expression.compile("${foo}${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ if ("foo".equals(c.getKey()))
+ assertEquals("foo", c.getKey());
+ if ("bar".equals(c.getKey()))
+ assertEquals("bar", c.getKey());
+ }));
+ assertEquals("foobar", Expression.compile("foo${foo}${bar}bar", STRICT_ESCAPES).evaluate((c, b) -> {
+ if ("foo".equals(c.getKey()))
+ assertEquals("foo", c.getKey());
+ if ("bar".equals(c.getKey()))
+ assertEquals("bar", c.getKey());
+ }));
+ assertEquals("foo${foo}bar", Expression.compile("foo$${foo}${bar}bar", STRICT_ESCAPES).evaluate((c, b) -> {
+ if ("bar".equals(c.getKey()))
+ assertEquals("bar", c.getKey());
+ }));
+ assertEquals("foo${foo}bar", Expression.compile("foo$${foo${bar}}bar", STRICT_ESCAPES).evaluate((c, b) -> {
+ if ("bar".equals(c.getKey()))
+ assertEquals("bar", c.getKey());
+ }));
+ assertEquals("", Expression.compile("${}", STRICT_ESCAPES).evaluate((c, b) -> {
+ assertEquals("", c.getKey());
+ }));
+ assertEquals("", Expression.compile("${:}", STRICT_ESCAPES).evaluate((c, b) -> {
+ assertEquals("", c.getKey());
+ }));
+
+ assertEquals("${foo}", Expression.compile("\\${foo}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("${foo}bar", Expression.compile("\\${foo}bar", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("\\$\\{%s}", Expression.compile("\\$\\{%s}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo${bar}", Expression.compile("foo\\${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo\\${bar}", Expression.compile("foo\\\\${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+
+ assertEquals("foo\\${bar}", Expression.compile("foo\\$${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$${bar}", Expression.compile("foo$\\${bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ assertEquals("foo$$\\{bar}", Expression.compile("foo$$\\{bar}", STRICT_ESCAPES).evaluate((c, b) -> {
+ }));
+ }
}