This is an automated email from the ASF dual-hosted git repository.

dlmarion 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 35655cb  Separate API and impl (#91)
35655cb is described below

commit 35655cb7c8880a67c4b5ae0ad42dfcb9af7db707
Author: Dave Marion <[email protected]>
AuthorDate: Thu Dec 18 08:16:21 2025 -0500

    Separate API and impl (#91)
    
    Implemented java module feature.
    Moved impl classes to their own package.
    
    Closes #81
---
 .github/workflows/maven.yaml                       |  2 --
 README.md                                          |  7 +++--
 pom.xml                                            | 14 +++++-----
 src/build/ci/find-unapproved-public.sh             | 32 ----------------------
 src/it/antlr4-example/pom.xml                      |  6 ++--
 .../antlr/AccessExpressionAntlrEvaluator.java      |  2 +-
 src/main/java/module-info.java                     | 21 ++++++++++++++
 .../apache/accumulo/access/AccessEvaluator.java    |  7 +++--
 .../apache/accumulo/access/AccessExpression.java   | 25 ++++++++---------
 .../accumulo/access/ParsedAccessExpression.java    | 12 ++++----
 .../access/{ => impl}/AccessEvaluatorImpl.java     | 27 ++++++++++--------
 .../access/{ => impl}/AccessExpressionImpl.java    | 12 ++++----
 .../accumulo/access/{ => impl}/ByteUtils.java      |  2 +-
 .../accumulo/access/{ => impl}/BytesWrapper.java   |  6 ++--
 .../{ => impl}/MultiAccessEvaluatorImpl.java       | 20 ++++++++++----
 .../{ => impl}/ParsedAccessExpressionImpl.java     | 21 +++++++-------
 .../access/{ => impl}/ParserEvaluator.java         | 22 ++++++++++++---
 .../accumulo/access/{ => impl}/Tokenizer.java      | 26 ++++++++++--------
 .../access/{ => tests}/AccessEvaluatorTest.java    | 14 ++++++----
 .../{ => tests}/AccessExpressionBenchmark.java     |  8 ++++--
 .../access/{ => tests}/AccessExpressionTest.java   |  5 +++-
 .../access/{ => tests}/AuthorizationTest.java      |  3 +-
 .../{ => tests}/ParsedAccessExpressionTest.java    |  4 ++-
 23 files changed, 165 insertions(+), 133 deletions(-)

diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml
index 72e32d7..db0722e 100644
--- a/.github/workflows/maven.yaml
+++ b/.github/workflows/maven.yaml
@@ -50,8 +50,6 @@ jobs:
       run: src/build/ci/find-unapproved-chars.sh
     - name: Check for unapproved JUnit API usage
       run: src/build/ci/find-unapproved-junit.sh
-    - name: Check for unapproved public classes
-      run: src/build/ci/find-unapproved-public.sh
     - name: Build with Maven (Fast Build)
       timeout-minutes: 20
       run: mvn -B -V -e -ntp "-Dstyle.color=always" clean package 
dependency:resolve -DskipTests -DskipFormat -DverifyFormat
diff --git a/README.md b/README.md
index cccfc26..702fad9 100644
--- a/README.md
+++ b/README.md
@@ -62,8 +62,9 @@ To run 
[AccessExample](src/test/java/example/AccessExample.java)
 
 ```
 mvn clean package
-CLASSPATH=$(ls target/accumulo-access-*.jar) java 
src/test/java/example/AccessExample.java
-CLASSPATH=$(ls target/accumulo-access-*.jar) java 
src/test/java/example/AccessExample.java RED BLUE
+
+java --module-path=$(ls target/accumulo-access-*.jar) 
--add-modules=accumulo.access src/test/java/example/AccessExample.java
+java --module-path=$(ls target/accumulo-access-*.jar) 
--add-modules=accumulo.access src/test/java/example/AccessExample.java RED BLUE
 ```
 
 Note that `data6` is always returned, because it has no access expression. And
@@ -73,7 +74,7 @@ To run 
[ParseExamples](src/test/java/example/ParseExamples.java)
 
 ```
 mvn clean package
-CLASSPATH=$(ls target/accumulo-access-*.jar) java 
src/test/java/example/ParseExamples.java
+java --module-path=$(ls target/accumulo-access-*.jar) 
--add-modules=accumulo.access src/test/java/example/ParseExamples.java
 ```
 
 
diff --git a/pom.xml b/pom.xml
index 9bdc161..5fb4358 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,9 +88,9 @@
   </ciManagement>
   <properties>
     <extraTestArgs>--add-opens java.base/java.lang=ALL-UNNAMED --add-opens 
java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED 
--add-opens java.base/java.net=ALL-UNNAMED --add-opens 
java.management/java.lang.management=ALL-UNNAMED --add-opens 
java.management/sun.management=ALL-UNNAMED --add-opens 
java.base/java.security=ALL-UNNAMED --add-opens 
java.base/java.lang.reflect=ALL-UNNAMED --add-opens 
java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.ut [...]
-    <maven.compiler.release>11</maven.compiler.release>
-    <maven.compiler.source>11</maven.compiler.source>
-    <maven.compiler.target>11</maven.compiler.target>
+    <maven.compiler.release>17</maven.compiler.release>
+    <maven.compiler.source>17</maven.compiler.source>
+    <maven.compiler.target>17</maven.compiler.target>
     <maven.javadoc.failOnWarnings>true</maven.javadoc.failOnWarnings>
     <maven.site.deploy.skip>true</maven.site.deploy.skip>
     <maven.site.skip>true</maven.site.skip>
@@ -113,7 +113,7 @@
     <dependency>
       <groupId>com.github.spotbugs</groupId>
       <artifactId>spotbugs-annotations</artifactId>
-      <version>4.8.3</version>
+      <version>4.9.3</version>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -289,7 +289,7 @@
       <plugin>
         <groupId>com.github.spotbugs</groupId>
         <artifactId>spotbugs-maven-plugin</artifactId>
-        <version>4.8.3.1</version>
+        <version>4.9.3.0</version>
         <configuration>
           
<excludeFilterFile>src/build/ci/spotbugs-exclude.xml</excludeFilterFile>
           <xmlOutput>true</xmlOutput>
@@ -414,7 +414,7 @@
           </checkstyleRules>
           <violationSeverity>warning</violationSeverity>
           <includeTestSourceDirectory>true</includeTestSourceDirectory>
-          <excludes>**/jmh_generated/</excludes>
+          <excludes>**/jmh_generated/,**/module-info.java</excludes>
         </configuration>
         <dependencies>
           <dependency>
@@ -778,7 +778,7 @@
                   <arguments>
                     <argument>-classpath</argument>
                     <classpath />
-                    
<argument>org.apache.accumulo.access.AccessExpressionBenchmark</argument>
+                    
<argument>org.apache.accumulo.access.tests.AccessExpressionBenchmark</argument>
                   </arguments>
                 </configuration>
               </execution>
diff --git a/src/build/ci/find-unapproved-public.sh 
b/src/build/ci/find-unapproved-public.sh
deleted file mode 100755
index 6a31983..0000000
--- a/src/build/ci/find-unapproved-public.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /usr/bin/env bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-count=$(grep -E "public.*(class|interface|enum|record)" 
src/main/java/org/apache/accumulo/access/*.java |
-  grep -v " interface AccessEvaluator " |
-  grep -v " class AccessExpression " |
-  grep -v " class ParsedAccessExpression " |
-  grep -v " enum ExpressionType " |
-  grep -v " class Authorizations " |
-  grep -c -v " class InvalidAccessExpressionException ")
-
-if [[ 0 -ne $count ]]; then
-  echo "$count unapproved public classes found"
-  exit 1
-fi
diff --git a/src/it/antlr4-example/pom.xml b/src/it/antlr4-example/pom.xml
index e14db9b..c1ace65 100644
--- a/src/it/antlr4-example/pom.xml
+++ b/src/it/antlr4-example/pom.xml
@@ -31,9 +31,9 @@
   <version>@project.version@</version>
   <name>Apache Accumulo Access Antlr4 Example</name>
   <properties>
-    <maven.compiler.release>11</maven.compiler.release>
-    <maven.compiler.source>11</maven.compiler.source>
-    <maven.compiler.target>11</maven.compiler.target>
+    <maven.compiler.release>17</maven.compiler.release>
+    <maven.compiler.source>17</maven.compiler.source>
+    <maven.compiler.target>17</maven.compiler.target>
     
<maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
diff --git 
a/src/it/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrEvaluator.java
 
b/src/it/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrEvaluator.java
index 5d20d8b..7120150 100644
--- 
a/src/it/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrEvaluator.java
+++ 
b/src/it/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrEvaluator.java
@@ -36,7 +36,7 @@ import 
org.apache.accumulo.access.grammars.AccessExpressionParser.And_operatorCo
 import 
org.apache.accumulo.access.grammars.AccessExpressionParser.Or_expressionContext;
 import 
org.apache.accumulo.access.grammars.AccessExpressionParser.Or_operatorContext;
 
-public class AccessExpressionAntlrEvaluator implements AccessEvaluator {
+public class AccessExpressionAntlrEvaluator {
 
   private class Entity {
 
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..537dec8
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module accumulo.access {
+  exports org.apache.accumulo.access;
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/accumulo/access/AccessEvaluator.java 
b/src/main/java/org/apache/accumulo/access/AccessEvaluator.java
index 6857343..ea0fb2c 100644
--- a/src/main/java/org/apache/accumulo/access/AccessEvaluator.java
+++ b/src/main/java/org/apache/accumulo/access/AccessEvaluator.java
@@ -20,6 +20,9 @@ package org.apache.accumulo.access;
 
 import java.util.Collection;
 
+import org.apache.accumulo.access.impl.AccessEvaluatorImpl;
+import org.apache.accumulo.access.impl.MultiAccessEvaluatorImpl;
+
 /**
  * This class is used to decide if an entity with a given set of 
authorizations can access
  * subsequent access expressions.
@@ -52,7 +55,7 @@ import java.util.Collection;
  * @see <a href="https://github.com/apache/accumulo-access";>Accumulo Access 
Documentation</a>
  * @since 1.0.0
  */
-public interface AccessEvaluator {
+public sealed interface AccessEvaluator permits AccessEvaluatorImpl, 
MultiAccessEvaluatorImpl {
 
   /**
    * @param accessExpression for this parameter a valid access expression is 
expected.
@@ -149,7 +152,7 @@ public interface AccessEvaluator {
    *
    */
   static AccessEvaluator of(Collection<Authorizations> authorizationSets) {
-    return new MultiAccessEvaluatorImpl(authorizationSets);
+    return MultiAccessEvaluatorImpl.of(authorizationSets);
   }
 
   /**
diff --git a/src/main/java/org/apache/accumulo/access/AccessExpression.java 
b/src/main/java/org/apache/accumulo/access/AccessExpression.java
index e9d8ab5..cf32670 100644
--- a/src/main/java/org/apache/accumulo/access/AccessExpression.java
+++ b/src/main/java/org/apache/accumulo/access/AccessExpression.java
@@ -25,6 +25,13 @@ import java.util.Arrays;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
+import org.apache.accumulo.access.impl.AccessEvaluatorImpl;
+import org.apache.accumulo.access.impl.AccessExpressionImpl;
+import org.apache.accumulo.access.impl.BytesWrapper;
+import org.apache.accumulo.access.impl.ParsedAccessExpressionImpl;
+import org.apache.accumulo.access.impl.ParserEvaluator;
+import org.apache.accumulo.access.impl.Tokenizer;
+
 /**
  * This class offers the ability to operate on access expressions.
  *
@@ -94,16 +101,12 @@ import java.util.function.Predicate;
  * @see <a href="https://github.com/apache/accumulo-access";>Accumulo Access 
Documentation</a>
  * @since 1.0.0
  */
-public abstract class AccessExpression implements Serializable {
+public sealed abstract class AccessExpression implements Serializable
+    permits AccessExpressionImpl, ParsedAccessExpression {
 
   private static final long serialVersionUID = 1L;
 
-  /*
-   * 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() {}
+  protected AccessExpression() {}
 
   /**
    * @return the expression that was used to create this object.
@@ -249,13 +252,7 @@ public abstract class AccessExpression implements 
Serializable {
    */
   public static void findAuthorizations(byte[] expression, Consumer<String> 
authorizationConsumer)
       throws InvalidAccessExpressionException {
-    var bytesWrapper = ParserEvaluator.lookupWrappers.get();
-    Predicate<Tokenizer.AuthorizationToken> atp = authToken -> {
-      bytesWrapper.set(authToken.data, authToken.start, authToken.len);
-      authorizationConsumer.accept(AccessEvaluatorImpl.unescape(bytesWrapper));
-      return true;
-    };
-    ParserEvaluator.parseAccessExpression(expression, atp, atp);
+    ParserEvaluator.findAuthorizations(expression, authorizationConsumer);
   }
 
   /**
diff --git 
a/src/main/java/org/apache/accumulo/access/ParsedAccessExpression.java 
b/src/main/java/org/apache/accumulo/access/ParsedAccessExpression.java
index a51e8ab..5928b1b 100644
--- a/src/main/java/org/apache/accumulo/access/ParsedAccessExpression.java
+++ b/src/main/java/org/apache/accumulo/access/ParsedAccessExpression.java
@@ -20,6 +20,8 @@ package org.apache.accumulo.access;
 
 import java.util.List;
 
+import org.apache.accumulo.access.impl.ParsedAccessExpressionImpl;
+
 /**
  * Instances of this class are immutable and wrap a verified access expression 
and a parse tree for
  * the access expression. To create an instance of this class call
@@ -28,16 +30,12 @@ import java.util.List;
  *
  * @since 1.0.0
  */
-public abstract class ParsedAccessExpression extends AccessExpression {
+public sealed abstract class ParsedAccessExpression extends AccessExpression
+    permits ParsedAccessExpressionImpl {
 
   private static final long serialVersionUID = 1L;
 
-  /*
-   * 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.
-   */
-  ParsedAccessExpression() {}
+  protected ParsedAccessExpression() {}
 
   /**
    * @since 1.0.0
diff --git a/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java 
b/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java
similarity index 84%
rename from src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java
rename to src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java
index 11f3f05..d5189c2 100644
--- a/src/main/java/org/apache/accumulo/access/AccessEvaluatorImpl.java
+++ b/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java
@@ -16,33 +16,38 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.accumulo.access.ByteUtils.BACKSLASH;
-import static org.apache.accumulo.access.ByteUtils.QUOTE;
-import static org.apache.accumulo.access.ByteUtils.isQuoteOrSlash;
-import static org.apache.accumulo.access.ByteUtils.isQuoteSymbol;
+import static org.apache.accumulo.access.impl.ByteUtils.BACKSLASH;
+import static org.apache.accumulo.access.impl.ByteUtils.QUOTE;
+import static org.apache.accumulo.access.impl.ByteUtils.isQuoteOrSlash;
+import static org.apache.accumulo.access.impl.ByteUtils.isQuoteSymbol;
 
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Predicate;
 
-//this class is intentionally package private and should never be made public
-final class AccessEvaluatorImpl implements AccessEvaluator {
+import org.apache.accumulo.access.AccessEvaluator;
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.Authorizations;
+import org.apache.accumulo.access.InvalidAccessExpressionException;
+
+public final class AccessEvaluatorImpl implements AccessEvaluator {
+
   private final Predicate<BytesWrapper> authorizedPredicate;
 
   /**
    * Create an AccessEvaluatorImpl using an Authorizer object
    */
-  AccessEvaluatorImpl(Authorizer authorizationChecker) {
+  public AccessEvaluatorImpl(Authorizer authorizationChecker) {
     this.authorizedPredicate = auth -> 
authorizationChecker.isAuthorized(unescape(auth));
   }
 
   /**
    * Create an AccessEvaluatorImpl using a collection of authorizations
    */
-  AccessEvaluatorImpl(Authorizations authorizations) {
+  public AccessEvaluatorImpl(Authorizations authorizations) {
     var authsSet = authorizations.asSet();
     final Set<BytesWrapper> authBytes = new HashSet<>(authsSet.size());
     for (String authorization : authsSet) {
@@ -56,7 +61,7 @@ final class AccessEvaluatorImpl implements AccessEvaluator {
     authorizedPredicate = authBytes::contains;
   }
 
-  static String unescape(BytesWrapper auth) {
+  public static String unescape(BytesWrapper auth) {
     int escapeCharCount = 0;
     for (int i = 0; i < auth.length(); i++) {
       byte b = auth.byteAt(i);
@@ -102,7 +107,7 @@ final class AccessEvaluatorImpl implements AccessEvaluator {
    * @param shouldQuote true to wrap escaped authorization in quotes
    * @return escaped authorization string
    */
-  static byte[] escape(byte[] auth, boolean shouldQuote) {
+  public static byte[] escape(byte[] auth, boolean shouldQuote) {
     int escapeCount = 0;
 
     for (byte value : auth) {
diff --git a/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java 
b/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java
similarity index 85%
rename from src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
rename to 
src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java
index 914e14a..9f60e33 100644
--- a/src/main/java/org/apache/accumulo/access/AccessExpressionImpl.java
+++ b/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java
@@ -16,27 +16,29 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.util.concurrent.atomic.AtomicReference;
 
-final class AccessExpressionImpl extends AccessExpression {
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.ParsedAccessExpression;
 
-  private static final long serialVersionUID = 1L;
+public final class AccessExpressionImpl extends AccessExpression {
 
+  private static final long serialVersionUID = 1L;
   public static final AccessExpression EMPTY = new AccessExpressionImpl("");
 
   private final String expression;
   private final AtomicReference<ParsedAccessExpression> parseTreeRef = new 
AtomicReference<>();
 
-  AccessExpressionImpl(String expression) {
+  public AccessExpressionImpl(String expression) {
     validate(expression);
     this.expression = expression;
   }
 
-  AccessExpressionImpl(byte[] expression) {
+  public AccessExpressionImpl(byte[] expression) {
     validate(expression);
     this.expression = new String(expression, UTF_8);
   }
diff --git a/src/main/java/org/apache/accumulo/access/ByteUtils.java 
b/src/main/java/org/apache/accumulo/access/impl/ByteUtils.java
similarity index 97%
rename from src/main/java/org/apache/accumulo/access/ByteUtils.java
rename to src/main/java/org/apache/accumulo/access/impl/ByteUtils.java
index 53dfc57..8f3c209 100644
--- a/src/main/java/org/apache/accumulo/access/ByteUtils.java
+++ b/src/main/java/org/apache/accumulo/access/impl/ByteUtils.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 /**
  * This class exists to avoid repeat conversions from byte to char as well as 
to provide helper
diff --git a/src/main/java/org/apache/accumulo/access/BytesWrapper.java 
b/src/main/java/org/apache/accumulo/access/impl/BytesWrapper.java
similarity index 94%
rename from src/main/java/org/apache/accumulo/access/BytesWrapper.java
rename to src/main/java/org/apache/accumulo/access/impl/BytesWrapper.java
index f9b53bb..e034f70 100644
--- a/src/main/java/org/apache/accumulo/access/BytesWrapper.java
+++ b/src/main/java/org/apache/accumulo/access/impl/BytesWrapper.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.checkFromIndexSize;
@@ -24,7 +24,7 @@ import static java.util.Objects.checkIndex;
 
 import java.util.Arrays;
 
-final class BytesWrapper implements Comparable<BytesWrapper> {
+public final class BytesWrapper implements Comparable<BytesWrapper> {
 
   private byte[] data;
   private int offset;
@@ -40,7 +40,7 @@ final class BytesWrapper implements Comparable<BytesWrapper> {
     set(data, 0, data.length);
   }
 
-  byte byteAt(int i) {
+  public byte byteAt(int i) {
     return data[offset + checkIndex(i, length)];
   }
 
diff --git 
a/src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java 
b/src/main/java/org/apache/accumulo/access/impl/MultiAccessEvaluatorImpl.java
similarity index 71%
rename from 
src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java
rename to 
src/main/java/org/apache/accumulo/access/impl/MultiAccessEvaluatorImpl.java
index 067310f..8c268f8 100644
--- a/src/main/java/org/apache/accumulo/access/MultiAccessEvaluatorImpl.java
+++ 
b/src/main/java/org/apache/accumulo/access/impl/MultiAccessEvaluatorImpl.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -24,10 +24,20 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
-class MultiAccessEvaluatorImpl implements AccessEvaluator {
-  final List<AccessEvaluatorImpl> evaluators;
+import org.apache.accumulo.access.AccessEvaluator;
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.Authorizations;
+import org.apache.accumulo.access.InvalidAccessExpressionException;
 
-  MultiAccessEvaluatorImpl(Collection<Authorizations> authorizationSets) {
+public final class MultiAccessEvaluatorImpl implements AccessEvaluator {
+
+  public static AccessEvaluator of(Collection<Authorizations> 
authorizationSets) {
+    return new MultiAccessEvaluatorImpl(authorizationSets);
+  }
+
+  private final List<AccessEvaluator> evaluators;
+
+  private MultiAccessEvaluatorImpl(Collection<Authorizations> 
authorizationSets) {
     evaluators = new ArrayList<>(authorizationSets.size());
     for (Authorizations authorizations : authorizationSets) {
       evaluators.add(new AccessEvaluatorImpl(authorizations));
@@ -41,7 +51,7 @@ class MultiAccessEvaluatorImpl implements AccessEvaluator {
 
   @Override
   public boolean canAccess(byte[] accessExpression) throws 
InvalidAccessExpressionException {
-    for (AccessEvaluatorImpl evaluator : evaluators) {
+    for (AccessEvaluator evaluator : evaluators) {
       if (!evaluator.canAccess(accessExpression)) {
         return false;
       }
diff --git 
a/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java 
b/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java
similarity index 87%
rename from 
src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
rename to 
src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java
index efb8cfc..99f6464 100644
--- a/src/main/java/org/apache/accumulo/access/ParsedAccessExpressionImpl.java
+++ 
b/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java
@@ -16,22 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.accumulo.access.ByteUtils.AND_OPERATOR;
-import static org.apache.accumulo.access.ByteUtils.OR_OPERATOR;
-import static org.apache.accumulo.access.ByteUtils.isAndOrOperator;
 import static 
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.AND;
 import static 
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.AUTHORIZATION;
 import static 
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.OR;
+import static org.apache.accumulo.access.impl.ByteUtils.AND_OPERATOR;
+import static org.apache.accumulo.access.impl.ByteUtils.OR_OPERATOR;
+import static org.apache.accumulo.access.impl.ByteUtils.isAndOrOperator;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
-// This class is intentionally package private
-final class ParsedAccessExpressionImpl extends ParsedAccessExpression {
+import org.apache.accumulo.access.ParsedAccessExpression;
+
+public final class ParsedAccessExpressionImpl extends ParsedAccessExpression {
 
   private static final long serialVersionUID = 1L;
 
@@ -44,9 +45,9 @@ final class ParsedAccessExpressionImpl extends 
ParsedAccessExpression {
 
   private final AtomicReference<String> stringExpression = new 
AtomicReference<>(null);
 
-  static final ParsedAccessExpression EMPTY = new ParsedAccessExpressionImpl();
+  public static final ParsedAccessExpression EMPTY = new 
ParsedAccessExpressionImpl();
 
-  ParsedAccessExpressionImpl(byte operator, byte[] expression, int offset, int 
length,
+  private ParsedAccessExpressionImpl(byte operator, byte[] expression, int 
offset, int length,
       List<ParsedAccessExpression> children) {
     if (children.isEmpty()) {
       throw new IllegalArgumentException("Must have children with an 
operator");
@@ -66,7 +67,7 @@ final class ParsedAccessExpressionImpl extends 
ParsedAccessExpression {
     this.children = List.copyOf(children);
   }
 
-  ParsedAccessExpressionImpl(byte[] expression, int offset, int length) {
+  private ParsedAccessExpressionImpl(byte[] expression, int offset, int 
length) {
     this.type = AUTHORIZATION;
     this.expression = expression;
     this.offset = offset;
@@ -108,7 +109,7 @@ final class ParsedAccessExpressionImpl extends 
ParsedAccessExpression {
     return children;
   }
 
-  static ParsedAccessExpression parseExpression(byte[] expression) {
+  public static ParsedAccessExpression parseExpression(byte[] expression) {
     if (expression.length == 0) {
       return ParsedAccessExpressionImpl.EMPTY;
     }
diff --git a/src/main/java/org/apache/accumulo/access/ParserEvaluator.java 
b/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java
similarity index 86%
rename from src/main/java/org/apache/accumulo/access/ParserEvaluator.java
rename to src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java
index 3ec67c1..2df624c 100644
--- a/src/main/java/org/apache/accumulo/access/ParserEvaluator.java
+++ b/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java
@@ -16,16 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
-import static org.apache.accumulo.access.ByteUtils.isAndOrOperator;
+import static org.apache.accumulo.access.impl.ByteUtils.isAndOrOperator;
 
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
+import org.apache.accumulo.access.InvalidAccessExpressionException;
+
 /**
  * Code for parsing and evaluating an access expression at the same time.
  */
-final class ParserEvaluator {
+public final class ParserEvaluator {
 
   static final byte OPEN_PAREN = (byte) '(';
   static final byte CLOSE_PAREN = (byte) ')';
@@ -37,7 +40,18 @@ final class ParserEvaluator {
   private static final ThreadLocal<Tokenizer> tokenizers =
       ThreadLocal.withInitial(() -> new Tokenizer(EMPTY));
 
-  static boolean parseAccessExpression(byte[] expression,
+  public static void findAuthorizations(byte[] expression, Consumer<String> 
authorizationConsumer)
+      throws InvalidAccessExpressionException {
+    var bytesWrapper = ParserEvaluator.lookupWrappers.get();
+    Predicate<Tokenizer.AuthorizationToken> atp = authToken -> {
+      bytesWrapper.set(authToken.data, authToken.start, authToken.len);
+      authorizationConsumer.accept(AccessEvaluatorImpl.unescape(bytesWrapper));
+      return true;
+    };
+    ParserEvaluator.parseAccessExpression(expression, atp, atp);
+  }
+
+  public static boolean parseAccessExpression(byte[] expression,
       Predicate<Tokenizer.AuthorizationToken> authorizedPredicate,
       Predicate<Tokenizer.AuthorizationToken> shortCircuitPredicate) {
     var tokenizer = tokenizers.get();
diff --git a/src/main/java/org/apache/accumulo/access/Tokenizer.java 
b/src/main/java/org/apache/accumulo/access/impl/Tokenizer.java
similarity index 87%
rename from src/main/java/org/apache/accumulo/access/Tokenizer.java
rename to src/main/java/org/apache/accumulo/access/impl/Tokenizer.java
index 2b11c92..da5b6ab 100644
--- a/src/main/java/org/apache/accumulo/access/Tokenizer.java
+++ b/src/main/java/org/apache/accumulo/access/impl/Tokenizer.java
@@ -16,21 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.impl;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.accumulo.access.ByteUtils.isBackslashSymbol;
-import static org.apache.accumulo.access.ByteUtils.isQuoteOrSlash;
-import static org.apache.accumulo.access.ByteUtils.isQuoteSymbol;
+import static org.apache.accumulo.access.impl.ByteUtils.isBackslashSymbol;
+import static org.apache.accumulo.access.impl.ByteUtils.isQuoteOrSlash;
+import static org.apache.accumulo.access.impl.ByteUtils.isQuoteSymbol;
 
 import java.util.stream.IntStream;
 
+import org.apache.accumulo.access.InvalidAccessExpressionException;
+
 /**
  * A simple wrapper around a byte array that keeps some state and provides 
high level operations to
  * the {@link ParserEvaluator} class. The purpose of this class is to make 
{@link ParserEvaluator}
  * as simple and easy to understand as possible while still being performant.
  */
-final class Tokenizer {
+public final class Tokenizer {
 
   private static final boolean[] validAuthChars = new boolean[256];
 
@@ -45,7 +47,7 @@ final class Tokenizer {
     "_-:./".chars().forEach(c -> validAuthChars[c] = true);
   }
 
-  static boolean isValidAuthChar(byte b) {
+  public static boolean isValidAuthChar(byte b) {
     return validAuthChars[0xff & b];
   }
 
@@ -54,10 +56,10 @@ final class Tokenizer {
 
   private final AuthorizationToken authorizationToken = new 
AuthorizationToken();
 
-  static class AuthorizationToken {
-    byte[] data;
-    int start;
-    int len;
+  public static class AuthorizationToken {
+    public byte[] data;
+    public int start;
+    public int len;
   }
 
   Tokenizer(byte[] expression) {
@@ -65,7 +67,7 @@ final class Tokenizer {
     authorizationToken.data = expression;
   }
 
-  public void reset(byte[] expression) {
+  void reset(byte[] expression) {
     this.expression = expression;
     authorizationToken.data = expression;
     this.index = 0;
@@ -102,7 +104,7 @@ final class Tokenizer {
     return expression[index];
   }
 
-  public byte[] expression() {
+  byte[] expression() {
     return expression;
   }
 
diff --git a/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java 
b/src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java
similarity index 94%
rename from src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java
rename to 
src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java
index b71ccb5..35a0d5c 100644
--- a/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java
+++ b/src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.tests;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.accumulo.access.AccessExpression.quote;
@@ -34,6 +34,12 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.accumulo.access.AccessEvaluator;
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.Authorizations;
+import org.apache.accumulo.access.InvalidAccessExpressionException;
+import org.apache.accumulo.access.impl.AccessEvaluatorImpl;
+import org.apache.accumulo.access.impl.BytesWrapper;
 import org.junit.jupiter.api.Test;
 
 import com.google.gson.Gson;
@@ -41,6 +47,8 @@ import com.google.gson.reflect.TypeToken;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
+@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
+    justification = "Field is written by Gson")
 public class AccessEvaluatorTest {
 
   enum ExpectedResult {
@@ -71,8 +79,6 @@ public class AccessEvaluatorTest {
     }
   }
 
-  @SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
-      justification = "Field is written by Gson")
   @Test
   public void runTestCases() throws IOException {
     List<TestDataSet> testData = readTestData();
@@ -100,8 +106,6 @@ public class AccessEvaluatorTest {
 
   }
 
-  @SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
-      justification = "Field is written by Gson")
   private static void runTestCases(TestDataSet testSet, AccessEvaluator 
evaluator) {
 
     assertFalse(testSet.tests.isEmpty());
diff --git 
a/src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java 
b/src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java
similarity index 96%
rename from 
src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java
rename to 
src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java
index 5940b5d..64de076 100644
--- a/src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java
+++ 
b/src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.tests;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -28,6 +28,9 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.accumulo.access.AccessEvaluator;
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.Authorizations;
 import org.apache.accumulo.core.security.ColumnVisibility;
 import org.apache.accumulo.core.security.VisibilityEvaluator;
 import org.apache.accumulo.core.security.VisibilityParseException;
@@ -58,6 +61,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  *
  * </blockquote>
  */
+@SuppressFBWarnings(value = {"NP_UNWRITTEN_FIELD"}, justification = "Field is 
written by Gson")
 public class AccessExpressionBenchmark {
 
   public static class VisibilityEvaluatorTests {
@@ -83,8 +87,6 @@ public class AccessExpressionBenchmark {
 
     private ArrayList<VisibilityEvaluatorTests> visibilityEvaluatorTests;
 
-    @SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"},
-        justification = "Field is written by Gson")
     @Setup
     public void loadData() throws IOException {
       List<AccessEvaluatorTest.TestDataSet> testData = 
AccessEvaluatorTest.readTestData();
diff --git a/src/test/java/org/apache/accumulo/access/AccessExpressionTest.java 
b/src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java
similarity index 97%
rename from src/test/java/org/apache/accumulo/access/AccessExpressionTest.java
rename to 
src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java
index af921ac..4bec427 100644
--- a/src/test/java/org/apache/accumulo/access/AccessExpressionTest.java
+++ b/src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.tests;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.stream.Collectors.toList;
@@ -39,6 +39,9 @@ import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.InvalidAccessExpressionException;
+import org.apache.accumulo.access.ParsedAccessExpression;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.function.Executable;
 
diff --git a/src/test/java/org/apache/accumulo/access/AuthorizationTest.java 
b/src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java
similarity index 95%
rename from src/test/java/org/apache/accumulo/access/AuthorizationTest.java
rename to src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java
index 1e1e764..53d8164 100644
--- a/src/test/java/org/apache/accumulo/access/AuthorizationTest.java
+++ b/src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.tests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertSame;
 
 import java.util.Set;
 
+import org.apache.accumulo.access.Authorizations;
 import org.junit.jupiter.api.Test;
 
 public class AuthorizationTest {
diff --git 
a/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java 
b/src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java
similarity index 96%
rename from 
src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
rename to 
src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java
index a5cc065..3ef9c55 100644
--- a/src/test/java/org/apache/accumulo/access/ParsedAccessExpressionTest.java
+++ 
b/src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.accumulo.access;
+package org.apache.accumulo.access.tests;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static 
org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.AND;
@@ -30,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.List;
 
+import org.apache.accumulo.access.AccessExpression;
+import org.apache.accumulo.access.ParsedAccessExpression;
 import org.junit.jupiter.api.Test;
 
 public class ParsedAccessExpressionTest {


Reply via email to