From a9181d8fe598918b75203d27d69096b27e8464c1 Mon Sep 17 00:00:00 2001 From: Vikas Saurabh Date: Sat, 22 Oct 2016 03:01:37 +0530 Subject: [PATCH] JCR-4042: Adding Escape Character in GQL Allow to escape \, ", and : using backslash as escape sequence prefix --- .../org/apache/jackrabbit/commons/query/GQL.java | 25 +++++++++-- .../apache/jackrabbit/commons/query/GQLTest.java | 50 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) mode change 100644 => 100755 jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java mode change 100644 => 100755 jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/query/GQLTest.java diff --git a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java old mode 100644 new mode 100755 index e749e5b..9773f76 --- a/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java +++ b/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/query/GQL.java @@ -94,6 +94,12 @@ import org.apache.jackrabbit.util.Text; * property name and the value. This also means that a value that contains * a colon does not need to be enclosed in double quotes. *

+ * Escaping + *

+ * To imply double-quotes("), backslash(\), and colon(:) literally you can + * escape them with a backslash. E.g. similar to example above in quoting for colon, + * "jcr:title":apache is equiavalent to jcr\:title:apache. + *

* Auto prefixes *

* When a property, node or node type name does not have a namespace prefix GQL @@ -717,6 +723,7 @@ public final class GQL { StringBuffer property = new StringBuffer(); StringBuffer value = new StringBuffer(); boolean quoted = false; + boolean escaped = false; boolean optional = false; for (char c : stmt) { switch (c) { @@ -739,7 +746,7 @@ public final class GQL { } break; case ':': - if (quoted) { + if (quoted || escaped) { value.append(c); } else { if (property.length() == 0) { @@ -753,7 +760,17 @@ public final class GQL { } break; case '"': - quoted = !quoted; + if (escaped) { + value.append(c); + } else { + quoted = !quoted; + } + break; + case '\\': + if (escaped) { + value.append(c); + } + escaped = !escaped; break; // noise case '*': @@ -770,11 +787,13 @@ public final class GQL { case '{': case '}': case '!': - case '\\': break; default: value.append(c); } + if (c != '\\') { + escaped = false; + } } } diff --git a/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/query/GQLTest.java b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/query/GQLTest.java old mode 100644 new mode 100755 index 1c6e98f..e3b2681 --- a/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/query/GQLTest.java +++ b/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/query/GQLTest.java @@ -41,4 +41,54 @@ public class GQLTest extends TestCase { } + public void testEscaping() throws RepositoryException { + //simple things work + assertEquals("//*[jcr:contains(assets/@a, 'b')] order by @jcr:score descending", + GQL.translateToXPath("a:b", null, "assets")); + + //backslash is ignored (same as earlier) and only ", \ and : are escaped + assertEquals("//*[jcr:contains(assets/@a, 'b')] order by @jcr:score descending", + GQL.translateToXPath("a:b\\", null, "assets")); + assertEquals("//*[jcr:contains(assets, 'ab')] order by @jcr:score descending", + GQL.translateToXPath("a\\b", null, "assets")); + assertEquals("//*[jcr:contains(assets/@a, 'b')] order by @jcr:score descending", + GQL.translateToXPath("a:\\b", null, "assets")); + + //backward compatibility of quoted ":" + assertEquals("//*[jcr:contains(assets/@a, '1:1')] order by @jcr:score descending", + GQL.translateToXPath("a:\"1:1\"", null, "assets")); + + //escaping ":" + assertEquals("//*[jcr:contains(assets/@a, '1:1')] order by @jcr:score descending", + GQL.translateToXPath("a:\"1\\:1\"", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a, '1:1')] order by @jcr:score descending", + GQL.translateToXPath("a:1\\:1", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a:, '1')] order by @jcr:score descending", + GQL.translateToXPath("a\\::1", null, "assets")); + + //escaping \ + assertEquals("//*[jcr:contains(assets/@a, '1\\1')] order by @jcr:score descending", + GQL.translateToXPath("a:\"1\\\\1\"", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a, '1\\1')] order by @jcr:score descending", + GQL.translateToXPath("a:1\\\\1", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a_x005c_, '1')] order by @jcr:score descending", + GQL.translateToXPath("a\\\\:1", null, "assets")); + + + //escaping " + assertEquals("//*[jcr:contains(assets/@a, '1\"1')] order by @jcr:score descending", + GQL.translateToXPath("a:\"1\\\"1\"", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a, '1\"1')] order by @jcr:score descending", + GQL.translateToXPath("a:1\\\"1", null, "assets")); + + assertEquals("//*[jcr:contains(assets/@a_x0022_, '1')] order by @jcr:score descending", + GQL.translateToXPath("a\\\":1", null, "assets")); + + } + } -- 2.8.3