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 <ktur...@apache.org>
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());

Reply via email to