This is an automated email from the ASF dual-hosted git repository.
kturner pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo-access.git
The following commit(s) were added to refs/heads/main by this push:
new 2f68281 memoizes creating a parse tree (#84)
2f68281 is described below
commit 2f6828151bffada062ec82faacfa6164a1f4f473
Author: Keith Turner <[email protected]>
AuthorDate: Tue Feb 11 18:32:22 2025 -0500
memoizes creating a parse tree (#84)
This change offers a new parse() method on AccessExpression that creates
a parse tree if one does not exits. This change allows decoupling how
access expression are created (w/ or w/o an initial parse tree) from
using parse trees.
For example code the like the following that wanted to use a parse tree
to validate an access expression would have required the
ParsedAccessExpression type to be passed to it. Now it can accept an
AccessExpression and call parse() which will do the most efficient thing
depending on how the expression was created.
```java
void checkExpression(AccessExpression expression){
var parsed = expression.parse();
// use parse tree to validate expression
}
```
---
.../org/apache/accumulo/access/AccessExpression.java | 13 ++++++++++++-
.../apache/accumulo/access/AccessExpressionImpl.java | 13 +++++++++++++
.../accumulo/access/ParsedAccessExpressionImpl.java | 5 +++++
.../accumulo/access/ParsedAccessExpressionTest.java | 18 +++++++++++++++++-
4 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/apache/accumulo/access/AccessExpression.java
b/src/main/java/org/apache/accumulo/access/AccessExpression.java
index 2bdc90e..6f0f18b 100644
--- a/src/main/java/org/apache/accumulo/access/AccessExpression.java
+++ b/src/main/java/org/apache/accumulo/access/AccessExpression.java
@@ -107,6 +107,15 @@ public abstract class AccessExpression implements
Serializable {
*/
public abstract String getExpression();
+ /**
+ * Parses the access expression if it was never parsed before. If this
access expression was
+ * created using {@link #parse(String)} or {@link #parse(byte[])} then it
will have a parse from
+ * inception and this method will return itself. If the access expression
was created using
+ * {@link #of(String)} or {@link #of(byte[])} then this method will create a
parse tree the first
+ * time its called and remember it, returning the remembered parse tree on
subsequent calls.
+ */
+ public abstract ParsedAccessExpression parse();
+
@Override
public boolean equals(Object o) {
if (o instanceof AccessExpression) {
@@ -170,7 +179,9 @@ public abstract class AccessExpression implements
Serializable {
/**
* Validates an access expression and returns an immutable object with a
parse tree. Creating the
* parse tree is expensive relative to calling {@link #of(String)} or {@link
#validate(String)},
- * so only use this method when the parse tree is needed.
+ * so only use this method when the parse tree is always needed. If the code
may only use the
+ * parse tree sometimes, then it may be best to call {@link #of(String)} to
create the access
+ * expression and then call {@link AccessExpression#parse()} when needed.
*
* @throws NullPointerException when the argument is null
* @throws InvalidAccessExpressionException if the given expression is not
valid
diff --git a/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
b/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
index 7a031b8..5dbddfd 100644
--- a/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
+++ b/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
@@ -27,6 +27,7 @@ final class AccessExpressionImpl extends AccessExpression {
public static final AccessExpression EMPTY = new AccessExpressionImpl("");
private final String expression;
+ private volatile ParsedAccessExpression parsed = null;
AccessExpressionImpl(String expression) {
validate(expression);
@@ -42,4 +43,16 @@ final class AccessExpressionImpl extends AccessExpression {
public String getExpression() {
return expression;
}
+
+ @Override
+ public ParsedAccessExpression parse() {
+ if (parsed == null) {
+ synchronized (this) {
+ if (parsed == null) {
+ parsed =
ParsedAccessExpressionImpl.parseExpression(expression.getBytes(UTF_8));
+ }
+ }
+ }
+ return parsed;
+ }
}
diff --git
a/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
b/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
index b6e316b..efb8cfc 100644
--- a/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
+++ b/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
@@ -93,6 +93,11 @@ final class ParsedAccessExpressionImpl extends
ParsedAccessExpression {
return stringExpression.get();
}
+ @Override
+ public ParsedAccessExpression parse() {
+ return this;
+ }
+
@Override
public ExpressionType getType() {
return type;
diff --git
a/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
b/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
index 27eae1a..a5cc065 100644
--- a/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
+++ b/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
@@ -24,6 +24,8 @@ import static
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.A
import static
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.EMPTY;
import static
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.OR;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.List;
@@ -35,7 +37,8 @@ public class ParsedAccessExpressionTest {
public void testParsing() {
String expression =
"(BLUE&(RED|PINK|YELLOW))|((YELLOW|\"GREEN/GREY\")&(RED|BLUE))|BLACK";
for (var parsed : List.of(AccessExpression.parse(expression),
- AccessExpression.parse(expression.getBytes(UTF_8)))) {
+ AccessExpression.parse(expression.getBytes(UTF_8)),
AccessExpression.of(expression).parse(),
+ AccessExpression.of(expression.getBytes(UTF_8)).parse())) {
// verify root node
verify("(BLUE&(RED|PINK|YELLOW))|((YELLOW|\"GREEN/GREY\")&(RED|BLUE))|BLACK",
OR, 3, parsed);
@@ -69,6 +72,18 @@ public class ParsedAccessExpressionTest {
verify("", EMPTY, 0, parsed);
}
+ @Test
+ public void testParseTwice() {
+ for (var expression : List.of(AccessExpression.of("A&B"),
+ AccessExpression.of("A&B".getBytes(UTF_8)))) {
+ var parsed = expression.parse();
+ assertNotSame(expression, parsed);
+ assertEquals(expression.getExpression(), parsed.getExpression());
+ assertSame(parsed, expression.parse());
+ assertSame(parsed, expression.parse());
+ }
+ }
+
/**
* Traverses a path in the parse tree an verifies the node at the end of the
path.
*/
@@ -81,6 +96,7 @@ public class ParsedAccessExpressionTest {
assertEquals(expectedExpression, parsed.getExpression());
assertEquals(expectedType, parsed.getType());
assertEquals(expectedChildren, parsed.getChildren().size());
+ assertSame(parsed, parsed.parse());
// check list of children is immutable
var fp = parsed;
assertThrows(UnsupportedOperationException.class, () ->
fp.getChildren().clear());