keith-turner commented on code in PR #83:
URL: https://github.com/apache/accumulo-access/pull/83#discussion_r1940238422


##########
src/main/java/org/apache/accumulo/access/AccessExpression.java:
##########
@@ -87,109 +97,131 @@
  * @see <a href="https://github.com/apache/accumulo-access";>Accumulo Access 
Documentation</a>
  * @since 1.0.0
  */
-public interface AccessExpression extends Serializable {
+public abstract class AccessExpression implements Serializable {
+  /*
+   * This is package private so that it can not be extended by classes outside 
of this package and
+   * create a mutable implementation. In this package all implementations that 
extends are
+   * immutable.
+   */
+  AccessExpression() {}
 
   /**
    * @return the expression that was used to create this object.
    */
-  String getExpression();
+  public abstract String getExpression();
 
-  /**
-   * @return the unique set of authorizations that occur in the expression. 
For example, for the
-   *         expression {@code (A&B)|(A&C)|(A&D)}, this method would return 
{@code [A,B,C,D]}.
-   */
-  Authorizations getAuthorizations();
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof AccessExpression) {
+      return ((AccessExpression) o).getExpression().equals(getExpression());
+    }
 
-  /**
-   * This is equivalent to calling {@code AccessExpression.of(expression, 
false);}
-   */
-  static AccessExpression of(String expression) throws 
InvalidAccessExpressionException {
-    return new AccessExpressionImpl(expression, false);
+    return false;
   }
 
-  /**
-   * <p>
-   * Validates an access expression and creates an immutable AccessExpression 
object.
-   *
-   * <p>
-   * When the {@code normalize} parameter is true, then will deduplicate, 
sort, flatten, and remove
-   * unneeded parentheses or quotes in the expressions. Normalization is done 
in addition to
-   * validation. The following list gives examples of what each normalization 
step does.
-   *
-   * <ul>
-   * <li>As an example of flattening, the expression {@code A&(B&C)} flattens 
to {@code A&B&C}.</li>
-   * <li>As an example of sorting, the expression {@code (Z&Y)|(C&B)} sorts to
-   * {@code (B&C)|(Y&Z)}</li>
-   * <li>As an example of deduplication, the expression {@code X&Y&X} 
normalizes to {@code X&Y}</li>
-   * <li>As an example of unneeded quotes, the expression {@code "ABC"&"XYZ"} 
normalizes to
-   * {@code ABC&XYZ}</li>
-   * <li>As an example of unneeded parentheses, the expression {@code 
(((ABC)|(XYZ)))} normalizes to
-   * {@code ABC|XYZ}</li>
-   * </ul>
-   *
-   * @param expression an access expression
-   * @param normalize If true then the expression will be normalized, if false 
the expression will
-   *        only be validated. Normalization is expensive so only use when 
needed. If repeatedly
-   *        normalizing expressions, consider using a cache that maps 
un-normalized expressions to
-   *        normalized ones. Since the normalization process is deterministic, 
the computation can
-   *        be cached.
-   * @throws InvalidAccessExpressionException when the expression is not valid.
-   */
-  static AccessExpression of(String expression, boolean normalize)
-      throws InvalidAccessExpressionException {
-    return new AccessExpressionImpl(expression, normalize);
+  @Override
+  public int hashCode() {
+    return getExpression().hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return getExpression();
   }
 
   /**
-   * <p>
    * This is equivalent to calling {@code AccessExpression.of(expression, 
false);}
    */
-  static AccessExpression of(byte[] expression) throws 
InvalidAccessExpressionException {
-    return new AccessExpressionImpl(expression, false);
+  public static AccessExpression of(String expression) throws 
InvalidAccessExpressionException {
+    return new AccessExpressionImpl(expression);
   }
 
   /**
    * <p>
-   * Validates an access expression and creates an immutable AccessExpression 
object.
-   *
-   * <p>
-   * If only validation is needed, then call {@link #validate(byte[])} because 
it will avoid copying
-   * the expression like this method does. This method must copy the byte 
array into a String in
-   * order to create an immutable AccessExpression.
-   *
-   * @see #of(String, boolean) for information about normlization.
-   * @param expression an access expression that is expected to be encoded 
using UTF-8
-   * @param normalize If true then the expression will be normalized, if false 
the expression will
-   *        only be validated. Normalization is expensive so only use when 
needed.
-   * @throws InvalidAccessExpressionException when the expression is not valid.
+   * This is equivalent to calling {@code AccessExpression.of(expression, 
false);}
    */
-  static AccessExpression of(byte[] expression, boolean normalize)
-      throws InvalidAccessExpressionException {
-    return new AccessExpressionImpl(expression, normalize);
+  public static AccessExpression of(byte[] expression) throws 
InvalidAccessExpressionException {
+    return new AccessExpressionImpl(expression);
   }
 
   /**
    * @return an empty AccessExpression that is immutable.
    */
-  static AccessExpression of() {
+  public static AccessExpression of() {
     return AccessExpressionImpl.EMPTY;
   }
 
+  public static ParsedAccessExpression parse(byte[] expression) {
+    if (expression.length == 0) {
+      return ParsedAccessExpressionImpl.EMPTY;
+    }
+
+    return 
ParsedAccessExpressionImpl.parseExpression(Arrays.copyOf(expression, 
expression.length));
+  }
+
+  public static ParsedAccessExpression parse(String expression) {
+    if (expression.isEmpty()) {
+      return ParsedAccessExpressionImpl.EMPTY;
+    }
+    return 
ParsedAccessExpressionImpl.parseExpression(expression.getBytes(UTF_8));
+  }
+
   /**
    * Quickly validates that an access expression is properly formed.
    *
    * @param expression a potential access expression that is expected to be 
encoded using UTF-8
    * @throws InvalidAccessExpressionException if the given expression is not 
valid
    */
-  static void validate(byte[] expression) throws 
InvalidAccessExpressionException {
-    AccessExpressionImpl.validate(expression);
+  public static void validate(byte[] expression) throws 
InvalidAccessExpressionException {
+    if (expression.length > 0) {
+      Tokenizer tokenizer = new Tokenizer(expression);
+      Predicate<Tokenizer.AuthorizationToken> atp = authToken -> true;
+      ParserEvaluator.parseAccessExpression(tokenizer, atp, atp);
+    } // else empty expression is valid, avoid object allocation
   }
 
   /**
    * @see #validate(byte[])
    */
-  static void validate(String expression) throws 
InvalidAccessExpressionException {
-    AccessExpressionImpl.validate(expression);
+  public static void validate(String expression) throws 
InvalidAccessExpressionException {
+    if (!expression.isEmpty()) {
+      validate(expression.getBytes(UTF_8));
+    } // else empty expression is valid, avoid object allocation
+  }
+
+  /**
+   * Validates and access expression and finds all authorizations in it 
passing them to the
+   * authorizationConsumer. For example, for the expression {@code 
(A&B)|(A&C)|(A&D)}, this method
+   * would pass {@code A,B,A,C,A,D} to the consumer one at a time. The 
function will conceptually
+   * call {@link #unquote(String)} prior to passing an authorization to 
authorizationConsumer.
+   *
+   * <p>
+   * What this method does could also be accomplished by creating a parse tree 
using
+   * {@link AccessExpression#parse(String)} and then recursively walking the 
parse tree. The
+   * implementation of this method does not create a parse tree and is much 
faster. If a parse tree
+   * is already available, then it would likely be faster to use it rather 
than call this method.
+   * </p>
+   *
+   * @throws InvalidAccessExpressionException when the expression is not valid.
+   */
+  public static void findAuthorizations(String expression, Consumer<String> 
authorizationConsumer)
+      throws InvalidAccessExpressionException {
+    findAuthorizations(expression.getBytes(UTF_8), authorizationConsumer);
+  }
+
+  /**
+   * @see #findAuthorizations(String, Consumer)
+   */
+  public static void findAuthorizations(byte[] expression, Consumer<String> 
authorizationConsumer)
+      throws InvalidAccessExpressionException {
+    Tokenizer tokenizer = new Tokenizer(expression);
+    Predicate<Tokenizer.AuthorizationToken> atp = authToken -> {
+      // TODO avoid creating BytesWrapper obj
+      authorizationConsumer.accept(AccessEvaluatorImpl
+          .unescape(new BytesWrapper(authToken.data, authToken.start, 
authToken.len)));
+      return true;
+    };
+    ParserEvaluator.parseAccessExpression(tokenizer, atp, atp);

Review Comment:
   Reused some code in de115b.  Was able to consolidate the code that avoids 
recreating Tokenizer objects, that was nice.   Some further consolidation of 
that code may be possible, but it would be a larger refactor.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to