terms = multiTermsRequest.getTerms();
- assertEquals(term1, terms.get(0));
- assertEquals(term2, terms.get(1));
- }
-
@Test
public void test_errorResponse() throws IOException {
MockResponse response = TestUtils.generateErrorMockResponse(TestUtils.getResourceFileContent("response_error.json"), 400);
diff --git a/src/test/resources/response_generateStrings.json b/src/test/resources/response_generateStrings.json
deleted file mode 100644
index 9ee8883..0000000
--- a/src/test/resources/response_generateStrings.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "type": "strings",
- "value": [
- "abcde",
- "dede",
- "deabc",
- "abcabc"
- ]
-}
\ No newline at end of file
diff --git a/src/test/resources/response_getDetails.json b/src/test/resources/response_getDetails.json
deleted file mode 100644
index 65e0539..0000000
--- a/src/test/resources/response_getDetails.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "type": "details",
- "cardinality": {
- "type": "Integer",
- "value": 2
- },
- "length": [
- 2,
- 3
- ],
- "empty": false,
- "total": false
-}
\ No newline at end of file
diff --git a/src/test/resources/response_intersection.json b/src/test/resources/response_intersection.json
deleted file mode 100644
index e6b1a7a..0000000
--- a/src/test/resources/response_intersection.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "regex",
- "value": "deabc"
-}
\ No newline at end of file
diff --git a/src/test/resources/response_isEquivalentTo.json b/src/test/resources/response_isEquivalentTo.json
deleted file mode 100644
index 25147f3..0000000
--- a/src/test/resources/response_isEquivalentTo.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "boolean",
- "value": false
-}
\ No newline at end of file
diff --git a/src/test/resources/response_isSubsetOf.json b/src/test/resources/response_isSubsetOf.json
deleted file mode 100644
index 84ed493..0000000
--- a/src/test/resources/response_isSubsetOf.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "boolean",
- "value": true
-}
\ No newline at end of file
diff --git a/src/test/resources/response_subtraction.json b/src/test/resources/response_subtraction.json
deleted file mode 100644
index 478ac72..0000000
--- a/src/test/resources/response_subtraction.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "regex",
- "value": "abc"
-}
\ No newline at end of file
diff --git a/src/test/resources/response_union.json b/src/test/resources/response_union.json
deleted file mode 100644
index 27dae5e..0000000
--- a/src/test/resources/response_union.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "regex",
- "value": "(abc|de|fghi)"
-}
\ No newline at end of file
From 985bc1b9b611faecea193275ea00daa148b5ea6d Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Mon, 20 Oct 2025 22:09:41 +0200
Subject: [PATCH 02/24] some fixes
---
README.md | 11 +++++++----
src/main/java/com/regexsolver/api/Request.java | 9 ---------
.../java/com/regexsolver/api/ResponseFormat.java | 12 ++++++++++++
src/main/java/com/regexsolver/api/Term.java | 1 -
.../java/com/regexsolver/api/IntegrationTest.java | 1 -
5 files changed, 19 insertions(+), 15 deletions(-)
create mode 100644 src/main/java/com/regexsolver/api/ResponseFormat.java
diff --git a/README.md b/README.md
index d73a5ae..e4fe10a 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,9 @@ implementation "com.regexsolver.api:RegexSolver:1.1.0"
2. Initialize the client and start working with terms:
```java
+import com.regexsolver.api.RegexSolver;
+import com.regexsolver.api.Term;
+
// Set REGEXSOLVER_API_TOKEN in your env and call initialize(),
// or pass the token directly:
RegexSolver.initialize(); // or RegexSolver.initialize("YOUR_API_TOKEN");
@@ -66,8 +69,8 @@ The API can handle terms in two formats:
By default, the engine returns whatever the operation produces, with no extra convertion. Override with `responseFormat`:
```java
-import com.regexsolver.Term;
-import com.regexsolver.ResponseFormat;
+import com.regexsolver.api.Term;
+import com.regexsolver.api.ResponseFormat;
Term term = Term.regex("abcde");
@@ -93,8 +96,8 @@ Regardless of the format, you can always call `getPattern()` to obtain the regex
Set a server-side compute timeout in milliseconds with `executionTimeout`:
```java
-import com.regexsolver.ApiError;
-import com.regexsolver.Term;
+import com.regexsolver.api.exception.ApiError;
+import com.regexsolver.api.Term;
try {
Term out = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c")
diff --git a/src/main/java/com/regexsolver/api/Request.java b/src/main/java/com/regexsolver/api/Request.java
index e252b9a..1aedbf4 100644
--- a/src/main/java/com/regexsolver/api/Request.java
+++ b/src/main/java/com/regexsolver/api/Request.java
@@ -74,15 +74,6 @@ public Integer getTimeout() {
return timeout;
}
}
-
- public enum ResponseFormat {
- @JsonProperty("any")
- ANY,
- @JsonProperty("regex")
- REGEX,
- @JsonProperty("fair")
- FAIR
- }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
diff --git a/src/main/java/com/regexsolver/api/ResponseFormat.java b/src/main/java/com/regexsolver/api/ResponseFormat.java
new file mode 100644
index 0000000..a1aeee5
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/ResponseFormat.java
@@ -0,0 +1,12 @@
+package com.regexsolver.api;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public enum ResponseFormat {
+ @JsonProperty("any")
+ ANY,
+ @JsonProperty("regex")
+ REGEX,
+ @JsonProperty("fair")
+ FAIR
+}
diff --git a/src/main/java/com/regexsolver/api/Term.java b/src/main/java/com/regexsolver/api/Term.java
index 1556d21..444dcba 100644
--- a/src/main/java/com/regexsolver/api/Term.java
+++ b/src/main/java/com/regexsolver/api/Term.java
@@ -6,7 +6,6 @@
import com.regexsolver.api.Request.MultiTermsRequest;
import com.regexsolver.api.Request.RepeatRequest;
import com.regexsolver.api.Request.RequestOptions;
-import com.regexsolver.api.Request.RequestOptions.ResponseFormat;
import com.regexsolver.api.dto.Cardinality;
import com.regexsolver.api.dto.Details;
import com.regexsolver.api.dto.Length;
diff --git a/src/test/java/com/regexsolver/api/IntegrationTest.java b/src/test/java/com/regexsolver/api/IntegrationTest.java
index fbac55c..652d5b3 100644
--- a/src/test/java/com/regexsolver/api/IntegrationTest.java
+++ b/src/test/java/com/regexsolver/api/IntegrationTest.java
@@ -1,6 +1,5 @@
package com.regexsolver.api;
-import com.regexsolver.api.Request.RequestOptions.ResponseFormat;
import com.regexsolver.api.Term.OperationOptions;
import com.regexsolver.api.dto.Cardinality;
import com.regexsolver.api.dto.Details;
From d85592ea541cf10d88cc5105010de9a9bf640736 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Tue, 21 Oct 2025 16:46:31 +0200
Subject: [PATCH 03/24] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e4fe10a..e22d87d 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ System.out.println(result1.toString()); // regex=(abc)?de
operationOptions = OperationOptions.init()
.responseFormat(ResponseFormat.FAIR);
-Term result2 = term.intersection(operationOptions, Term.regex("de.*"));
+Term result2 = term.union(operationOptions, Term.regex("de"));
System.out.println(r2.toString()); // fair=...
```
From 190fd083169208add089df7507788fdbb7353aa6 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Tue, 21 Oct 2025 16:47:58 +0200
Subject: [PATCH 04/24] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index e22d87d..532d2ee 100644
--- a/README.md
+++ b/README.md
@@ -78,13 +78,13 @@ OperationOptions operationOptions = OperationOptions.init()
.responseFormat(ResponseFormat.REGEX);
Term result1 = term.union(operationOptions, Term.regex("de"));
-System.out.println(result1.toString()); // regex=(abc)?de
+System.out.println(result1); // regex=(abc)?de
operationOptions = OperationOptions.init()
.responseFormat(ResponseFormat.FAIR);
Term result2 = term.union(operationOptions, Term.regex("de"));
-System.out.println(r2.toString()); // fair=...
+System.out.println(result2); // fair=...
```
If the format does not matter, omit `responseFormat` or set it to `ResponseFormat.ANY`.
From 179ad19af16188f35c4a60a41f2305c36b7a5783 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Tue, 21 Oct 2025 17:00:32 +0200
Subject: [PATCH 05/24] Update README.md
---
README.md | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 532d2ee..8d89786 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,7 @@ By default, the engine returns whatever the operation produces, with no extra co
```java
import com.regexsolver.api.Term;
+import com.regexsolver.api.OperationOptions;
import com.regexsolver.api.ResponseFormat;
Term term = Term.regex("abcde");
@@ -97,11 +98,17 @@ Set a server-side compute timeout in milliseconds with `executionTimeout`:
```java
import com.regexsolver.api.exception.ApiError;
+import com.regexsolver.api.OperationOptions;
import com.regexsolver.api.Term;
+// Limit the server-side compute time to 5 ms
try {
- Term out = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c")
- .difference(Term.regex(".*abc.*"));
+ Term term1 = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c");
+ Term term2 = Term.regex(".*abc.*");
+
+ OperationOptions operationOptions = OperationOptions.init()
+ .executionTimeout(5);
+ Term out = term1.difference(operationOptions, term2);
} catch (ApiError e) {
System.out.println(e.getMessage()); // The operation took too much time.
}
From 7168fd70640c7438487adb1491ce456e109f9ad5 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Tue, 21 Oct 2025 20:41:42 +0200
Subject: [PATCH 06/24] Reorganize classes
---
.github/workflows/maven.yml | 2 ++
README.md | 6 ++--
.../com/regexsolver/api/OperationOptions.java | 28 +++++++++++++++
src/main/java/com/regexsolver/api/Term.java | 29 +--------------
.../com/regexsolver/api/package-info.java | 2 +-
.../com/regexsolver/api/IntegrationTest.java | 35 ++++++++++++++-----
6 files changed, 61 insertions(+), 41 deletions(-)
create mode 100644 src/main/java/com/regexsolver/api/OperationOptions.java
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 159c41c..968404f 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -18,4 +18,6 @@ jobs:
distribution: 'temurin'
cache: maven
- name: Test
+ env:
+ REGEXSOLVER_API_TOKEN: ${{ secrets.REGEXSOLVER_API_TOKEN }}
run: mvn test
diff --git a/README.md b/README.md
index 8d89786..614c101 100644
--- a/README.md
+++ b/README.md
@@ -75,13 +75,13 @@ import com.regexsolver.api.ResponseFormat;
Term term = Term.regex("abcde");
-OperationOptions operationOptions = OperationOptions.init()
+OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result1 = term.union(operationOptions, Term.regex("de"));
System.out.println(result1); // regex=(abc)?de
-operationOptions = OperationOptions.init()
+operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.FAIR);
Term result2 = term.union(operationOptions, Term.regex("de"));
@@ -106,7 +106,7 @@ try {
Term term1 = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c");
Term term2 = Term.regex(".*abc.*");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.executionTimeout(5);
Term out = term1.difference(operationOptions, term2);
} catch (ApiError e) {
diff --git a/src/main/java/com/regexsolver/api/OperationOptions.java b/src/main/java/com/regexsolver/api/OperationOptions.java
new file mode 100644
index 0000000..836e039
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/OperationOptions.java
@@ -0,0 +1,28 @@
+package com.regexsolver.api;
+
+public class OperationOptions {
+ protected ResponseFormat responseFormat;
+ protected Integer executionTimeout;
+
+ public static OperationOptions newDefault() {
+ return new OperationOptions();
+ }
+
+ public OperationOptions responseFormat(ResponseFormat responseFormat) {
+ this.responseFormat = responseFormat;
+ return this;
+ }
+
+ public ResponseFormat responseFormat() {
+ return responseFormat;
+ }
+
+ public OperationOptions executionTimeout(Integer executionTimeout) {
+ this.executionTimeout = executionTimeout;
+ return this;
+ }
+
+ public Integer executionTimeout() {
+ return executionTimeout;
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/Term.java b/src/main/java/com/regexsolver/api/Term.java
index 444dcba..fff3657 100644
--- a/src/main/java/com/regexsolver/api/Term.java
+++ b/src/main/java/com/regexsolver/api/Term.java
@@ -87,7 +87,7 @@ public String getValue() {
private static RequestOptions loadRequestOptions(OperationOptions opts) {
RequestOptions requestOptions = null;
if (opts != null) {
- requestOptions = RequestOptions.fromArgs(opts.responseFormat, opts.executionTimeout);
+ requestOptions = RequestOptions.fromArgs(opts.responseFormat(), opts.executionTimeout());
}
return requestOptions;
}
@@ -626,31 +626,4 @@ public String getPattern() {
return getValue();
}
}
-
- public static final class OperationOptions {
- private ResponseFormat responseFormat;
- private Integer executionTimeout;
-
- public static OperationOptions init() {
- return new OperationOptions();
- }
-
- public OperationOptions responseFormat(ResponseFormat responseFormat) {
- this.responseFormat = responseFormat;
- return this;
- }
-
- public ResponseFormat responseFormat() {
- return responseFormat;
- }
-
- public OperationOptions executionTimeout(Integer executionTimeout) {
- this.executionTimeout = executionTimeout;
- return this;
- }
-
- public Integer executionTimeout() {
- return executionTimeout;
- }
- }
}
diff --git a/src/main/java/com/regexsolver/api/package-info.java b/src/main/java/com/regexsolver/api/package-info.java
index 1870e39..62b3b26 100644
--- a/src/main/java/com/regexsolver/api/package-info.java
+++ b/src/main/java/com/regexsolver/api/package-info.java
@@ -2,7 +2,7 @@
* Contains all the classes you need to start using the library.
*
* To start using this library you need to first request an API token at RegexSolver Console,
- * then call RegexSolverApiWrapper.initialize("YOUR_TOKEN") to set it.
+ * set it as environment variable in REGEXSOLVER_API_TOKEN then call RegexSolverApiWrapper.initialize().
*
*
* You can find some examples in our documentation.
diff --git a/src/test/java/com/regexsolver/api/IntegrationTest.java b/src/test/java/com/regexsolver/api/IntegrationTest.java
index 652d5b3..9536909 100644
--- a/src/test/java/com/regexsolver/api/IntegrationTest.java
+++ b/src/test/java/com/regexsolver/api/IntegrationTest.java
@@ -1,9 +1,9 @@
package com.regexsolver.api;
-import com.regexsolver.api.Term.OperationOptions;
import com.regexsolver.api.dto.Cardinality;
import com.regexsolver.api.dto.Details;
import com.regexsolver.api.dto.Length;
+import com.regexsolver.api.exception.ApiError;
import org.junit.Before;
import org.junit.Test;
@@ -127,7 +127,7 @@ public void test_analyze_subset() throws Exception {
public void test_compute_concat() throws Exception {
Term term1 = Term.regex("abc");
Term term2 = Term.regex("de");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result = term1.concat(operationOptions, term2);
assertEquals("regex=abcde", result.toString());
@@ -137,7 +137,7 @@ public void test_compute_concat() throws Exception {
public void test_compute_difference() throws Exception {
Term term1 = Term.regex("(abc|de)");
Term term2 = Term.regex("de");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result = term1.difference(operationOptions, term2);
assertEquals("regex=abc", result.toString());
@@ -148,7 +148,7 @@ public void test_compute_intersection() throws Exception {
Term term1 = Term.regex("(abc|de){2}");
Term term2 = Term.regex("de.*");
Term term3 = Term.regex(".*abc");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result = term1.intersection(operationOptions, term2, term3);
assertEquals("regex=deabc", result.toString());
@@ -157,7 +157,7 @@ public void test_compute_intersection() throws Exception {
@Test
public void test_compute_repeat() throws Exception {
Term term = Term.regex("abc");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result = term.repeat(operationOptions, 3, 5);
assertEquals("regex=(abc){3,5}", result.toString());
@@ -168,7 +168,7 @@ public void test_compute_union() throws Exception {
Term term1 = Term.regex("abc");
Term term2 = Term.regex("de");
Term term3 = Term.regex("fghi");
- OperationOptions operationOptions = OperationOptions.init()
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result = term1.union(operationOptions, term2, term3);
assertEquals("regex=(abc|de|fghi)", result.toString());
@@ -199,14 +199,31 @@ public void test_readme_quickstart() throws Exception {
@Test
public void test_readme_response_format() throws Exception {
Term term = Term.regex("abcde");
- OperationOptions operationOptions = OperationOptions.init()
+
+ OperationOptions operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.REGEX);
Term result1 = term.union(operationOptions, Term.regex("de"));
+
assertEquals("regex=(abc)?de", result1.toString());
- operationOptions = OperationOptions.init()
+ operationOptions = OperationOptions.newDefault()
.responseFormat(ResponseFormat.FAIR);
- Term result2 = term.intersection(operationOptions, Term.regex("de.*"));
+ Term result2 = term.union(operationOptions, Term.regex("de"));
+
assertTrue(result2.toString().startsWith("fair="));
}
+
+ @Test
+ public void test_readme_execution_timeout() throws Exception {
+ try {
+ Term term1 = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c");
+ Term term2 = Term.regex(".*abc.*");
+
+ OperationOptions operationOptions = OperationOptions.newDefault()
+ .executionTimeout(5);
+ term1.difference(operationOptions, term2);
+ } catch (ApiError e) {
+ System.out.println(e.getMessage());
+ }
+ }
}
\ No newline at end of file
From f4f2c30f933e361fed48ba5eaeff2c3cdbd76ef4 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Sun, 26 Oct 2025 14:59:09 +0100
Subject: [PATCH 07/24] Remove getDetails
---
.../api/RegexSolverApiWrapper.java | 45 ++++------
.../com/regexsolver/api/ResponseContent.java | 2 -
src/main/java/com/regexsolver/api/Term.java | 29 ------
.../java/com/regexsolver/api/dto/Details.java | 88 -------------------
.../exception/MissingAPITokenException.java | 4 +-
.../com/regexsolver/api/IntegrationTest.java | 28 ------
6 files changed, 18 insertions(+), 178 deletions(-)
delete mode 100644 src/main/java/com/regexsolver/api/dto/Details.java
diff --git a/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java b/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
index 1facd0a..f2332af 100644
--- a/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
+++ b/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
@@ -8,7 +8,6 @@
import com.regexsolver.api.Response.StringResponse;
import com.regexsolver.api.Response.StringsResponse;
import com.regexsolver.api.dto.Cardinality;
-import com.regexsolver.api.dto.Details;
import com.regexsolver.api.dto.Length;
import com.regexsolver.api.exception.ApiError;
import com.regexsolver.api.exception.MissingAPITokenException;
@@ -30,7 +29,7 @@
final class RegexSolverApiWrapper {
private static final RegexSolverApiWrapper INSTANCE = new RegexSolverApiWrapper();
- private final static String DEFAULT_BASE_URL = "https://api.regexsolver.com/";
+ private final static String DEFAULT_BASE_URL = "https://api.regexsolver.com/v1/";
private final static String USER_AGENT = "RegexSolver Java / 1.1.0";
@@ -90,15 +89,6 @@ public Cardinality analyzeCardinality(Term term) throws ApiError, IOException {
}
}
- public Details analyzeDetails(Term term) throws ApiError, IOException {
- Response response = api.analyzeDetails(term).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
public String analyzeDot(Term term) throws ApiError, IOException {
Response response = api.analyzeDot(term).execute();
if (response.isSuccessful()) {
@@ -240,54 +230,51 @@ private static ApiError getApiError(Response response) throws IOException
private interface RegexApi {
// analyze
- @POST("api/analyze/cardinality")
+ @POST("analyze/cardinality")
Call analyzeCardinality(@Body Term term);
- @POST("api/analyze/details")
- Call analyzeDetails(@Body Term term);
-
- @POST("api/analyze/dot")
+ @POST("analyze/dot")
Call analyzeDot(@Body Term term);
- @POST("api/analyze/equivalent")
+ @POST("analyze/equivalent")
Call analyzeEquivalent(@Body MultiTermsRequest multiTermsRequest);
- @POST("api/analyze/empty")
+ @POST("analyze/empty")
Call analyzeEmpty(@Body Term term);
- @POST("api/analyze/empty_string")
+ @POST("analyze/empty_string")
Call analyzeEmptyString(@Body Term term);
- @POST("api/analyze/length")
+ @POST("analyze/length")
Call analyzeLength(@Body Term term);
- @POST("api/analyze/pattern")
+ @POST("analyze/pattern")
Call analyzePattern(@Body Term term);
- @POST("api/analyze/subset")
+ @POST("analyze/subset")
Call analyzeSubset(@Body MultiTermsRequest multiTermsRequest);
- @POST("api/analyze/total")
+ @POST("analyze/total")
Call analyzeTotal(@Body Term term);
// compute
- @POST("api/compute/concat")
+ @POST("compute/concat")
Call computeConcat(@Body MultiTermsRequest multiTermsRequest);
- @POST("api/compute/difference")
+ @POST("compute/difference")
Call computeDifference(@Body MultiTermsRequest multiTermsRequest);
- @POST("api/compute/intersection")
+ @POST("compute/intersection")
Call computeIntersection(@Body MultiTermsRequest multiTermsRequest);
- @POST("api/compute/repeat")
+ @POST("compute/repeat")
Call computeRepeat(@Body RepeatRequest repeatRequest);
- @POST("api/compute/union")
+ @POST("compute/union")
Call computeUnion(@Body MultiTermsRequest multiTermsRequest);
// generate
- @POST("api/generate/strings")
+ @POST("generate/strings")
Call generateStrings(@Body GenerateStringsRequest request);
}
}
diff --git a/src/main/java/com/regexsolver/api/ResponseContent.java b/src/main/java/com/regexsolver/api/ResponseContent.java
index ea37121..5b4f260 100644
--- a/src/main/java/com/regexsolver/api/ResponseContent.java
+++ b/src/main/java/com/regexsolver/api/ResponseContent.java
@@ -5,13 +5,11 @@
import com.regexsolver.api.Response.BooleanResponse;
import com.regexsolver.api.Response.StringResponse;
import com.regexsolver.api.Response.StringsResponse;
-import com.regexsolver.api.dto.Details;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Term.Fair.class, name = "fair"),
@JsonSubTypes.Type(value = Term.Regex.class, name = "regex"),
- @JsonSubTypes.Type(value = Details.class, name = "details"),
@JsonSubTypes.Type(value = StringResponse.class, name = "string"),
@JsonSubTypes.Type(value = StringsResponse.class, name = "strings"),
@JsonSubTypes.Type(value = BooleanResponse.class, name = "boolean"),
diff --git a/src/main/java/com/regexsolver/api/Term.java b/src/main/java/com/regexsolver/api/Term.java
index fff3657..433f471 100644
--- a/src/main/java/com/regexsolver/api/Term.java
+++ b/src/main/java/com/regexsolver/api/Term.java
@@ -7,7 +7,6 @@
import com.regexsolver.api.Request.RepeatRequest;
import com.regexsolver.api.Request.RequestOptions;
import com.regexsolver.api.dto.Cardinality;
-import com.regexsolver.api.dto.Details;
import com.regexsolver.api.dto.Length;
import com.regexsolver.api.exception.ApiError;
@@ -34,8 +33,6 @@ public abstract class Term implements ResponseContent {
@JsonIgnore
private transient String serialized = null;
- @JsonIgnore
- private transient Details details;
@JsonIgnore
private transient Cardinality cardinality;
@JsonIgnore
@@ -137,32 +134,12 @@ public boolean equivalent(Term term) throws IOException, ApiError {
public Cardinality getCardinality() throws IOException, ApiError {
if (cardinality != null) {
return cardinality;
- } else if (details != null) {
- return details.getCardinality();
}
cardinality = RegexSolverApiWrapper.getInstance()
.analyzeCardinality(this);
return cardinality;
}
- /**
- * Get the details of this term.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return The details of this term.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Details getDetails() throws IOException, ApiError {
- if (details != null) {
- return details;
- }
- details = RegexSolverApiWrapper.getInstance().analyzeDetails(this);
- return details;
- }
-
/**
* Get the GraphViz DOT representation of this term.
* Cache the result to avoid calling the API again if this method is called
@@ -206,8 +183,6 @@ public String getFair() throws IOException, ApiError {
public Length getLength() throws IOException, ApiError {
if (length != null) {
return length;
- } else if (details != null) {
- return details.getLength();
}
length = RegexSolverApiWrapper.getInstance()
@@ -250,8 +225,6 @@ public String getPattern() throws IOException, ApiError {
public boolean isEmpty() throws IOException, ApiError {
if (empty != null) {
return empty;
- } else if (details != null) {
- return details.isEmpty();
}
empty = RegexSolverApiWrapper.getInstance()
@@ -292,8 +265,6 @@ public boolean isEmptyString() throws IOException, ApiError {
public boolean isTotal() throws IOException, ApiError {
if (total != null) {
return total;
- } else if (details != null) {
- return details.isTotal();
}
total = RegexSolverApiWrapper.getInstance()
diff --git a/src/main/java/com/regexsolver/api/dto/Details.java b/src/main/java/com/regexsolver/api/dto/Details.java
deleted file mode 100644
index 0d78d67..0000000
--- a/src/main/java/com/regexsolver/api/dto/Details.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.regexsolver.api.dto;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.regexsolver.api.ResponseContent;
-import com.regexsolver.api.Term;
-
-import java.util.Objects;
-
-/**
- * Contains details about the requested {@link Term}.
- */
-public final class Details implements ResponseContent {
- private final Cardinality cardinality;
- private final Length length;
- private final boolean empty;
- private final boolean total;
-
- /**
- * @param cardinality the number of possible values.
- * @param length the minimum and maximum length of possible values.
- * @param empty true if is an empty set (does not contain any value), false otherwise.
- * @param total true if is a total set (contains all values), false otherwise.
- */
- public Details(
- @JsonProperty("cardinality") Cardinality cardinality,
- @JsonProperty("length") Length length,
- @JsonProperty("empty") boolean empty,
- @JsonProperty("total") boolean total
- ) {
- this.cardinality = cardinality;
- this.length = length;
- this.empty = empty;
- this.total = total;
- }
-
- /**
- * @return The number of possible values.
- */
- public Cardinality getCardinality() {
- return cardinality;
- }
-
- /**
- * @return The minimum and maximum length of possible values.
- */
- public Length getLength() {
- return length;
- }
-
- /**
- * @return true if is an empty set (does not contain any value), false otherwise.
- */
- public boolean isEmpty() {
- return empty;
- }
-
- /**
- * @return true if is a total set (contains all values), false otherwise.
- */
- public boolean isTotal() {
- return total;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) return true;
- if (obj == null || obj.getClass() != this.getClass()) return false;
- var that = (Details) obj;
- return Objects.equals(this.cardinality, that.cardinality) &&
- Objects.equals(this.length, that.length) &&
- this.empty == that.empty &&
- this.total == that.total;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(cardinality, length, empty, total);
- }
-
- @Override
- public String toString() {
- return "Details[" +
- "cardinality=" + cardinality + ", " +
- "length=" + length + ", " +
- "empty=" + empty + ", " +
- "total=" + total + ']';
- }
-}
diff --git a/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java b/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
index ddac5ec..4240691 100644
--- a/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
+++ b/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
@@ -5,11 +5,11 @@
*/
public class MissingAPITokenException extends RuntimeException {
/**
- * The API token has not been set, call RegexSolverApiWrapper.initialize(\"YOUR_TOKEN\"); to set it.
+ * The API token has not been set, set the environment variable REGEXSOLVER_API_TOKEN and call RegexSolverApiWrapper.initialize() to set it.
* To generate a token go to RegexSolver Console.
*/
public MissingAPITokenException() {
- super("The API token has not been set, call RegexSolverApiWrapper.initialize(\"YOUR_TOKEN\") to set it.\n" +
+ super("The API token has not been set, set the environment variable REGEXSOLVER_API_TOKEN and call RegexSolverApiWrapper.initialize() to set it.\n" +
"To generate a token go to https://console.regexsolver.com/.");
}
}
diff --git a/src/test/java/com/regexsolver/api/IntegrationTest.java b/src/test/java/com/regexsolver/api/IntegrationTest.java
index 9536909..beba50d 100644
--- a/src/test/java/com/regexsolver/api/IntegrationTest.java
+++ b/src/test/java/com/regexsolver/api/IntegrationTest.java
@@ -1,7 +1,6 @@
package com.regexsolver.api;
import com.regexsolver.api.dto.Cardinality;
-import com.regexsolver.api.dto.Details;
import com.regexsolver.api.dto.Length;
import com.regexsolver.api.exception.ApiError;
@@ -28,33 +27,6 @@ public void test_analyze_cardinality() throws Exception {
assertEquals("Integer(5)", cardinality.toString());
}
- @Test
- public void test_analyze_details() throws Exception {
- Term term = Term.regex("(abc|de)");
- Details details = term.getDetails();
- assertEquals(
- "Details[cardinality=Integer(2), length=Length[minimum=2, maximum=3], empty=false, total=false]",
- details.toString());
- }
-
- @Test
- public void test_analyze_details_infinite() throws Exception {
- Term term = Term.regex(".*");
- Details details = term.getDetails();
- assertEquals(
- "Details[cardinality=Infinite, length=Length[minimum=0, maximum=null], empty=false, total=true]",
- details.toString());
- }
-
- @Test
- public void test_analyze_details_empty() throws Exception {
- Term term = Term.regex("[]");
- Details details = term.getDetails();
- assertEquals(
- "Details[cardinality=Integer(0), length=Length[minimum=null, maximum=null], empty=true, total=false]",
- details.toString());
- }
-
@Test
public void test_analyze_dot() throws Exception {
Term term = Term.regex("(abc|de)");
From cd5a2e7275064bbe24d939b2ab891e9057860181 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Sun, 26 Oct 2025 14:59:58 +0100
Subject: [PATCH 08/24] Remove getDetails
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 614c101..363ea28 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,6 @@ Timeout is best effort. The exact time is not guaranteed.
| -------- | ------- | ------- |
| `t.equivalent(Term term)` | `boolean` | `true` if `t` and `term` accept exactly the same language. Supports `executionTimeout`. |
| `t.getCardinality()` | `Cardinality` | Returns the cardinality of the term (i.e., the number of possible matched strings). |
-| `t.getDetails()` | `Details` | Returns cardinality, length bounds, and if it is empty or total. |
| `t.getDot()` | `String` | Returns a Graphviz DOT representation of the automaton for the term. |
| `t.getFair()` | `String` | Returns the FAIR of the term if defined. |
| `t.getLength()` | `Length` | Returns the minimum and maximum length of matched strings. |
From 5e48f47a82d64ba35e1f783cceb0eeeee0a570c1 Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Wed, 29 Oct 2025 19:44:49 +0100
Subject: [PATCH 09/24] update readme
---
README.md | 35 ++++++++++++++++++-----------------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/README.md b/README.md
index 363ea28..caf0f9e 100644
--- a/README.md
+++ b/README.md
@@ -34,18 +34,27 @@ implementation "com.regexsolver.api:RegexSolver:1.1.0"
```java
import com.regexsolver.api.RegexSolver;
import com.regexsolver.api.Term;
+import com.regexsolver.api.exception.ApiError;
+
+import java.io.IOException;
-// Set REGEXSOLVER_API_TOKEN in your env and call initialize(),
-// or pass the token directly:
-RegexSolver.initialize(); // or RegexSolver.initialize("YOUR_API_TOKEN");
+public class Main {
+ public static void main(String[] args) throws IOException, ApiError {
+ // Set REGEXSOLVER_API_TOKEN in your env and call initialize(),
+ // or pass the token directly:
+ RegexSolver.initialize(); // or RegexSolver.initialize("YOUR_API_TOKEN");
-Term term1 = Term.regex("(abc|de|fg){2,}");
-Term term2 = Term.regex("de.*");
-Term term3 = Term.regex(".*abc");
+ // Create terms
+ Term term1 = Term.regex("(abc|de|fg){2,}");
+ Term term2 = Term.regex("de.*");
+ Term term3 = Term.regex(".*abc");
-Term result = term1.intersection(term2, term3)
- .difference(Term.regex(".+(abc|de).+"));
-System.out.println(result.getPattern()); // de(fg)*abc
+ // Compute intersection and difference
+ Term result = term1.intersection(term2, term3)
+ .difference(Term.regex(".+(abc|de).+"));
+ System.out.println(result.getPattern()); // de(fg)*abc
+ }
+}
```
@@ -69,10 +78,6 @@ The API can handle terms in two formats:
By default, the engine returns whatever the operation produces, with no extra convertion. Override with `responseFormat`:
```java
-import com.regexsolver.api.Term;
-import com.regexsolver.api.OperationOptions;
-import com.regexsolver.api.ResponseFormat;
-
Term term = Term.regex("abcde");
OperationOptions operationOptions = OperationOptions.newDefault()
@@ -97,10 +102,6 @@ Regardless of the format, you can always call `getPattern()` to obtain the regex
Set a server-side compute timeout in milliseconds with `executionTimeout`:
```java
-import com.regexsolver.api.exception.ApiError;
-import com.regexsolver.api.OperationOptions;
-import com.regexsolver.api.Term;
-
// Limit the server-side compute time to 5 ms
try {
Term term1 = Term.regex(".*ab.*c(de|fg).*dab.*c(de|fg).*ab.*c(de|fg).*dab.*c");
From fe4244231adc322e54b6492aa14322a052fc1f2a Mon Sep 17 00:00:00 2001
From: Alexandre van Beurden <1949482+alexvbrdn@users.noreply.github.com>
Date: Sun, 29 Mar 2026 16:51:21 +0200
Subject: [PATCH 10/24] Update library
---
.github/workflows/maven.yml | 25 +-
.openapi-generator-ignore | 20 +
.openapi-generator/FILES | 45 +
.openapi-generator/VERSION | 1 +
generate-api.sh | 20 +
openapitools.json | 7 +
pom.xml | 102 +-
.../api/AsyncRegexSolverClient.java | 836 ++++++++++
.../java/com/regexsolver/api/Cardinality.java | 146 ++
src/main/java/com/regexsolver/api/Length.java | 76 +
.../com/regexsolver/api/OperationOptions.java | 28 -
.../java/com/regexsolver/api/RateLimiter.java | 54 +
.../java/com/regexsolver/api/RegexSolver.java | 15 -
.../api/RegexSolverApiWrapper.java | 280 ----
.../regexsolver/api/RegexSolverClient.java | 431 ++++++
.../java/com/regexsolver/api/Request.java | 161 --
.../java/com/regexsolver/api/Response.java | 43 -
.../com/regexsolver/api/ResponseContent.java | 18 -
.../com/regexsolver/api/ResponseFormat.java | 25 +-
src/main/java/com/regexsolver/api/Term.java | 642 ++------
.../regexsolver/api/TermPropertiesMixin.java | 41 +
.../com/regexsolver/api/dto/Cardinality.java | 86 --
.../java/com/regexsolver/api/dto/Length.java | 117 --
.../com/regexsolver/api/dto/package-info.java | 4 -
.../regexsolver/api/exception/ApiError.java | 19 -
.../exception/MissingAPITokenException.java | 15 -
.../api/exception/package-info.java | 4 -
.../api/exceptions/ApiException.java | 33 +
.../api/exceptions/BadRequestException.java | 14 +
.../api/exceptions/ForbiddenException.java | 14 +
.../exceptions/InternalServerException.java | 17 +
.../api/exceptions/InvalidJsonException.java | 14 +
...lidNumberOfStringsToGenerateException.java | 16 +
.../api/exceptions/InvalidTokenException.java | 14 +
.../MissingOrMalformedTokenException.java | 14 +
.../api/exceptions/NotFoundException.java | 16 +
.../exceptions/QuotaExceededException.java | 14 +
.../api/exceptions/RegexSolverException.java | 11 +
.../exceptions/TimeoutExceededException.java | 14 +
.../exceptions/TimeoutTooLargeException.java | 14 +
.../exceptions/TooManyRequestsException.java | 17 +
.../api/exceptions/TooManyTermsException.java | 14 +
.../api/exceptions/UnauthorizedException.java | 14 +
.../regexsolver/api/generated/ApiClient.java | 486 ++++++
.../api/generated/ApiException.java | 92 ++
.../api/generated/ApiResponse.java | 60 +
.../api/generated/Configuration.java | 63 +
.../com/regexsolver/api/generated/JSON.java | 261 ++++
.../com/regexsolver/api/generated/Pair.java | 37 +
.../api/generated/RFC3339DateFormat.java | 57 +
.../generated/RFC3339InstantDeserializer.java | 100 ++
.../api/generated/RFC3339JavaTimeModule.java | 39 +
.../api/generated/ServerConfiguration.java | 72 +
.../api/generated/ServerVariable.java | 37 +
.../api/generated/api/AnalyzeApi.java | 1362 +++++++++++++++++
.../api/generated/api/ComputeApi.java | 965 ++++++++++++
.../api/generated/api/GenerateApi.java | 302 ++++
.../model/AbstractOpenApiSchema.java | 144 ++
.../api/generated/model/BooleanDto.java | 217 +++
.../model/Cardinality200ResponseDto.java | 185 +++
.../model/CardinalityBigIntegerDto.java | 181 +++
.../api/generated/model/CardinalityDto.java | 362 +++++
.../model/CardinalityInfiniteDto.java | 181 +++
.../model/CardinalityIntegerDto.java | 218 +++
.../generated/model/Concat200ResponseDto.java | 185 +++
.../generated/model/Dot200ResponseDto.java | 185 +++
.../generated/model/Empty200ResponseDto.java | 185 +++
.../api/generated/model/ErrorResponseDto.java | 220 +++
.../generated/model/ExecutionOptionsDto.java | 149 ++
.../model/GenerateStringsRequestDto.java | 297 ++++
.../model/GenerateStringsResponseDto.java | 255 +++
.../generated/model/Length200ResponseDto.java | 185 +++
.../api/generated/model/LengthDto.java | 253 +++
.../generated/model/MultiTermsRequestDto.java | 201 +++
.../api/generated/model/RepeatRequestDto.java | 258 ++++
.../generated/model/RequestOptionsDto.java | 222 +++
.../generated/model/ResponseOptionsDto.java | 185 +++
.../api/generated/model/StringDto.java | 217 +++
.../model/Strings200ResponseDto.java | 185 +++
.../api/generated/model/StringsDto.java | 231 +++
.../api/generated/model/TermDto.java | 306 ++++
.../api/generated/model/TermFairDto.java | 217 +++
.../api/generated/model/TermRegexDto.java | 217 +++
.../api/generated/model/TermRequestDto.java | 186 +++
.../generated/model/TwoTermsRequestDto.java | 201 +++
.../com/regexsolver/api/package-info.java | 11 -
.../api/AsyncRegexSolverClientTest.java | 540 +++++++
.../com/regexsolver/api/IntegrationTest.java | 201 ---
.../java/com/regexsolver/api/ModelsTest.java | 220 +++
.../com/regexsolver/api/RateLimiterTest.java | 74 +
.../api/RegexSolverClientTest.java | 152 ++
.../regexsolver/api/TermOperationTest.java | 45 -
.../regexsolver/api/TermSerializeTest.java | 33 -
.../java/com/regexsolver/api/TestUtils.java | 47 -
src/test/resources/response_error.json | 4 -
95 files changed, 13133 insertions(+), 1666 deletions(-)
create mode 100644 .openapi-generator-ignore
create mode 100644 .openapi-generator/FILES
create mode 100644 .openapi-generator/VERSION
create mode 100755 generate-api.sh
create mode 100644 openapitools.json
create mode 100644 src/main/java/com/regexsolver/api/AsyncRegexSolverClient.java
create mode 100644 src/main/java/com/regexsolver/api/Cardinality.java
create mode 100644 src/main/java/com/regexsolver/api/Length.java
delete mode 100644 src/main/java/com/regexsolver/api/OperationOptions.java
create mode 100644 src/main/java/com/regexsolver/api/RateLimiter.java
delete mode 100644 src/main/java/com/regexsolver/api/RegexSolver.java
delete mode 100644 src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
create mode 100644 src/main/java/com/regexsolver/api/RegexSolverClient.java
delete mode 100644 src/main/java/com/regexsolver/api/Request.java
delete mode 100644 src/main/java/com/regexsolver/api/Response.java
delete mode 100644 src/main/java/com/regexsolver/api/ResponseContent.java
create mode 100644 src/main/java/com/regexsolver/api/TermPropertiesMixin.java
delete mode 100644 src/main/java/com/regexsolver/api/dto/Cardinality.java
delete mode 100644 src/main/java/com/regexsolver/api/dto/Length.java
delete mode 100644 src/main/java/com/regexsolver/api/dto/package-info.java
delete mode 100644 src/main/java/com/regexsolver/api/exception/ApiError.java
delete mode 100644 src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
delete mode 100644 src/main/java/com/regexsolver/api/exception/package-info.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/ApiException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/BadRequestException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/ForbiddenException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/InternalServerException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/InvalidJsonException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/InvalidNumberOfStringsToGenerateException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/InvalidTokenException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/MissingOrMalformedTokenException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/NotFoundException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/QuotaExceededException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/RegexSolverException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/TimeoutExceededException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/TimeoutTooLargeException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/TooManyRequestsException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/TooManyTermsException.java
create mode 100644 src/main/java/com/regexsolver/api/exceptions/UnauthorizedException.java
create mode 100644 src/main/java/com/regexsolver/api/generated/ApiClient.java
create mode 100644 src/main/java/com/regexsolver/api/generated/ApiException.java
create mode 100644 src/main/java/com/regexsolver/api/generated/ApiResponse.java
create mode 100644 src/main/java/com/regexsolver/api/generated/Configuration.java
create mode 100644 src/main/java/com/regexsolver/api/generated/JSON.java
create mode 100644 src/main/java/com/regexsolver/api/generated/Pair.java
create mode 100644 src/main/java/com/regexsolver/api/generated/RFC3339DateFormat.java
create mode 100644 src/main/java/com/regexsolver/api/generated/RFC3339InstantDeserializer.java
create mode 100644 src/main/java/com/regexsolver/api/generated/RFC3339JavaTimeModule.java
create mode 100644 src/main/java/com/regexsolver/api/generated/ServerConfiguration.java
create mode 100644 src/main/java/com/regexsolver/api/generated/ServerVariable.java
create mode 100644 src/main/java/com/regexsolver/api/generated/api/AnalyzeApi.java
create mode 100644 src/main/java/com/regexsolver/api/generated/api/ComputeApi.java
create mode 100644 src/main/java/com/regexsolver/api/generated/api/GenerateApi.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/AbstractOpenApiSchema.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/BooleanDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Cardinality200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/CardinalityBigIntegerDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/CardinalityDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/CardinalityInfiniteDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/CardinalityIntegerDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Concat200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Dot200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Empty200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/ErrorResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/ExecutionOptionsDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/GenerateStringsRequestDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/GenerateStringsResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Length200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/LengthDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/MultiTermsRequestDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/RepeatRequestDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/RequestOptionsDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/ResponseOptionsDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/StringDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/Strings200ResponseDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/StringsDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/TermDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/TermFairDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/TermRegexDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/TermRequestDto.java
create mode 100644 src/main/java/com/regexsolver/api/generated/model/TwoTermsRequestDto.java
delete mode 100644 src/main/java/com/regexsolver/api/package-info.java
create mode 100644 src/test/java/com/regexsolver/api/AsyncRegexSolverClientTest.java
delete mode 100644 src/test/java/com/regexsolver/api/IntegrationTest.java
create mode 100644 src/test/java/com/regexsolver/api/ModelsTest.java
create mode 100644 src/test/java/com/regexsolver/api/RateLimiterTest.java
create mode 100644 src/test/java/com/regexsolver/api/RegexSolverClientTest.java
delete mode 100644 src/test/java/com/regexsolver/api/TermOperationTest.java
delete mode 100644 src/test/java/com/regexsolver/api/TermSerializeTest.java
delete mode 100644 src/test/java/com/regexsolver/api/TestUtils.java
delete mode 100644 src/test/resources/response_error.json
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 968404f..abff5a2 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -1,23 +1,30 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+#
+# This file is auto-generated by OpenAPI Generator (https://openapi-generator.tech)
+
name: Java CI with Maven
on:
push:
- branches: [ "main" ]
+ branches: [ main, master ]
pull_request:
- branches: [ "main" ]
+ branches: [ main, master ]
jobs:
build:
+ name: Build RegexSolver
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ java: [ 17, 21 ]
steps:
- uses: actions/checkout@v4
- - name: Set up JDK 11
- uses: actions/setup-java@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v4
with:
- java-version: '11'
+ java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: maven
- - name: Test
- env:
- REGEXSOLVER_API_TOKEN: ${{ secrets.REGEXSOLVER_API_TOKEN }}
- run: mvn test
+ - name: Build with Maven
+ run: mvn -B package --no-transfer-progress --file pom.xml
diff --git a/.openapi-generator-ignore b/.openapi-generator-ignore
new file mode 100644
index 0000000..51baa4b
--- /dev/null
+++ b/.openapi-generator-ignore
@@ -0,0 +1,20 @@
+src/main/java/com/regexsolver/api/exceptions/**
+test/
+pom.xml
+.gitignore
+git_push.sh
+.travis.yml
+.gitlab-ci.yml
+pyproject.toml
+.github/
+docs/
+test/
+README.md
+build.gradle
+settings.gradle
+gradlew
+gradlew.bat
+gradle/
+gradle.properties
+build.sbt
+*.xml
diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES
new file mode 100644
index 0000000..77bda1d
--- /dev/null
+++ b/.openapi-generator/FILES
@@ -0,0 +1,45 @@
+api/openapi.yaml
+gradle.properties
+src/main/AndroidManifest.xml
+src/main/java/com/regexsolver/api/generated/ApiClient.java
+src/main/java/com/regexsolver/api/generated/ApiException.java
+src/main/java/com/regexsolver/api/generated/ApiResponse.java
+src/main/java/com/regexsolver/api/generated/Configuration.java
+src/main/java/com/regexsolver/api/generated/JSON.java
+src/main/java/com/regexsolver/api/generated/Pair.java
+src/main/java/com/regexsolver/api/generated/RFC3339DateFormat.java
+src/main/java/com/regexsolver/api/generated/RFC3339InstantDeserializer.java
+src/main/java/com/regexsolver/api/generated/RFC3339JavaTimeModule.java
+src/main/java/com/regexsolver/api/generated/ServerConfiguration.java
+src/main/java/com/regexsolver/api/generated/ServerVariable.java
+src/main/java/com/regexsolver/api/generated/api/AnalyzeApi.java
+src/main/java/com/regexsolver/api/generated/api/ComputeApi.java
+src/main/java/com/regexsolver/api/generated/api/GenerateApi.java
+src/main/java/com/regexsolver/api/generated/model/AbstractOpenApiSchema.java
+src/main/java/com/regexsolver/api/generated/model/BooleanDto.java
+src/main/java/com/regexsolver/api/generated/model/Cardinality200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/CardinalityBigIntegerDto.java
+src/main/java/com/regexsolver/api/generated/model/CardinalityDto.java
+src/main/java/com/regexsolver/api/generated/model/CardinalityInfiniteDto.java
+src/main/java/com/regexsolver/api/generated/model/CardinalityIntegerDto.java
+src/main/java/com/regexsolver/api/generated/model/Concat200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/Dot200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/Empty200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/ErrorResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/ExecutionOptionsDto.java
+src/main/java/com/regexsolver/api/generated/model/GenerateStringsRequestDto.java
+src/main/java/com/regexsolver/api/generated/model/GenerateStringsResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/Length200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/LengthDto.java
+src/main/java/com/regexsolver/api/generated/model/MultiTermsRequestDto.java
+src/main/java/com/regexsolver/api/generated/model/RepeatRequestDto.java
+src/main/java/com/regexsolver/api/generated/model/RequestOptionsDto.java
+src/main/java/com/regexsolver/api/generated/model/ResponseOptionsDto.java
+src/main/java/com/regexsolver/api/generated/model/StringDto.java
+src/main/java/com/regexsolver/api/generated/model/Strings200ResponseDto.java
+src/main/java/com/regexsolver/api/generated/model/StringsDto.java
+src/main/java/com/regexsolver/api/generated/model/TermDto.java
+src/main/java/com/regexsolver/api/generated/model/TermFairDto.java
+src/main/java/com/regexsolver/api/generated/model/TermRegexDto.java
+src/main/java/com/regexsolver/api/generated/model/TermRequestDto.java
+src/main/java/com/regexsolver/api/generated/model/TwoTermsRequestDto.java
diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION
new file mode 100644
index 0000000..a29ba3d
--- /dev/null
+++ b/.openapi-generator/VERSION
@@ -0,0 +1 @@
+7.21.0
diff --git a/generate-api.sh b/generate-api.sh
new file mode 100755
index 0000000..632f814
--- /dev/null
+++ b/generate-api.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+SPEC_FILE="../m-lab/shared/openapi.yaml"
+OUT_DIR="./"
+
+echo "Running openapi-generator-cli..."
+openapi-generator-cli generate \
+ -i "$SPEC_FILE" \
+ -g java \
+ -o "$OUT_DIR" \
+ --model-name-suffix Dto \
+ --additional-properties=library=native \
+ --additional-properties=invokerPackage=com.regexsolver.api.generated \
+ --additional-properties=apiPackage=com.regexsolver.api.generated.api \
+ --additional-properties=modelPackage=com.regexsolver.api.generated.model \
+ --additional-properties=asyncNative=true \
+ --additional-properties=useRuntimeException=true \
+ --additional-properties=openApiNullable=false
+
+echo "API Generation Complete."
diff --git a/openapitools.json b/openapitools.json
new file mode 100644
index 0000000..91d9c43
--- /dev/null
+++ b/openapitools.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
+ "spaces": 2,
+ "generator-cli": {
+ "version": "7.21.0"
+ }
+}
diff --git a/pom.xml b/pom.xml
index e284508..8aaa69b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,7 +1,9 @@
-
-
+
+
4.0.0
com.regexsolver.api
@@ -37,32 +39,93 @@
11
11
UTF-8
+
+ 2.21.1
+ 2.21
+ 1.3.5
+ 5.10.2
+ 5.11.0
+ 3.25.3
- com.squareup.retrofit2
- retrofit
- 2.12.0
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
- com.squareup.retrofit2
- converter-jackson
- 2.12.0
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.annotations.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
- junit
- junit
- 4.13.2
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson.version}
+
+
+
+ org.openapitools
+ jackson-databind-nullable
+ 0.2.9
+
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+ ${jakarta.annotation.version}
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
test
+
- com.squareup.okhttp3
- mockwebserver
- 3.14.9
+ org.assertj
+ assertj-core
+ ${assertj.version}
test
+
@@ -82,11 +145,6 @@
org.apache.maven.plugins
maven-javadoc-plugin
3.7.0
-
-
- **/ResponseContent.java
-
-
attach-javadocs
@@ -127,4 +185,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/java/com/regexsolver/api/AsyncRegexSolverClient.java b/src/main/java/com/regexsolver/api/AsyncRegexSolverClient.java
new file mode 100644
index 0000000..ab47830
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/AsyncRegexSolverClient.java
@@ -0,0 +1,836 @@
+package com.regexsolver.api;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.regexsolver.api.exceptions.*;
+import com.regexsolver.api.generated.ApiClient;
+import com.regexsolver.api.generated.ApiException;
+import com.regexsolver.api.generated.api.AnalyzeApi;
+import com.regexsolver.api.generated.api.ComputeApi;
+import com.regexsolver.api.generated.api.GenerateApi;
+import com.regexsolver.api.generated.model.*;
+import java.net.http.HttpHeaders;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * The Asynchronous Client for RegexSolver.
+ *
+ * Provides non-blocking access to all RegexSolver API endpoints.
+ */
+public final class AsyncRegexSolverClient {
+
+ private static final String VERSION = "1.1.0";
+ private final String apiToken;
+ private final String baseUrl;
+ private final RateLimiter rateLimiter;
+ private final AnalyzeApi analyzeApi;
+ private final ComputeApi computeApi;
+ private final GenerateApi generateApi;
+ private final ObjectMapper objectMapper;
+
+ private AsyncRegexSolverClient(Builder builder) {
+ this.apiToken = builder.apiToken;
+ this.baseUrl = builder.baseUrl;
+ this.rateLimiter = RateLimiter.getInstance(this.apiToken);
+
+ ApiClient apiClient = new ApiClient();
+ apiClient.setBasePath(this.baseUrl);
+ apiClient.setRequestInterceptor(requestBuilder -> {
+ requestBuilder.header(
+ "User-Agent",
+ "RegexSolver Java / " + VERSION
+ );
+ requestBuilder.header("Authorization", "Bearer " + this.apiToken);
+ });
+
+ this.analyzeApi = new AnalyzeApi(apiClient);
+ this.computeApi = new ComputeApi(apiClient);
+ this.generateApi = new GenerateApi(apiClient);
+ this.objectMapper = apiClient.getObjectMapper();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+
+ private String apiToken;
+ private String baseUrl = "https://api.regexsolver.com/v1";
+
+ public Builder apiToken(String apiToken) {
+ this.apiToken = apiToken;
+ return this;
+ }
+
+ public Builder baseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ return this;
+ }
+
+ public AsyncRegexSolverClient build() {
+ if (apiToken == null || apiToken.isEmpty()) {
+ throw new IllegalArgumentException("apiToken is required");
+ }
+ return new AsyncRegexSolverClient(this);
+ }
+ }
+
+ // --- INTERNAL HELPERS ---
+
+ private RequestOptionsDto buildOptions(
+ Integer timeout,
+ ResponseFormat format
+ ) {
+ RequestOptionsDto options = new RequestOptionsDto().schemaVersion(1);
+ if (timeout != null) {
+ options.execution(new ExecutionOptionsDto().timeout(timeout));
+ }
+ if (format != null) {
+ options.response(new ResponseOptionsDto().format(format.toDto()));
+ }
+ return options;
+ }
+
+ private CompletableFuture executeWithRetry(
+ Supplier> apiCall
+ ) {
+ return executeWithRetry(apiCall, 0);
+ }
+
+ private CompletableFuture executeWithRetry(
+ Supplier> apiCall,
+ int attempt
+ ) {
+ return rateLimiter
+ .waitIfNecessary()
+ .thenCompose(v -> apiCall.get())
+ .exceptionallyCompose(ex -> {
+ Throwable cause = ex.getCause() != null ? ex.getCause() : ex;
+ if (cause instanceof ApiException) {
+ ApiException apiEx = (ApiException) cause;
+ if (apiEx.getCode() == 429 && attempt < 5) {
+ double retryAfter = 1.0;
+ HttpHeaders headers = apiEx.getResponseHeaders();
+ if (headers != null) {
+ retryAfter = headers
+ .firstValue("Retry-After")
+ .map(Double::parseDouble)
+ .orElse(1.0);
+ }
+ rateLimiter.trigger(retryAfter);
+ return executeWithRetry(apiCall, attempt + 1);
+ }
+ throw mapException(apiEx);
+ }
+ if (
+ cause instanceof RuntimeException
+ ) throw (RuntimeException) cause;
+ throw new RuntimeException(cause);
+ });
+ }
+
+ private RegexSolverException mapException(ApiException ex) {
+ int code = ex.getCode();
+ String body = ex.getResponseBody();
+ String message = ex.getMessage();
+ String errorCode = "UnknownError";
+
+ try {
+ ErrorResponseDto errorResponse = objectMapper.readValue(
+ body,
+ ErrorResponseDto.class
+ );
+ if (errorResponse.getError() != null) message =
+ errorResponse.getError();
+ if (errorResponse.getErrorCode() != null) errorCode =
+ errorResponse.getErrorCode();
+ } catch (Exception ignored) {}
+
+ switch (code) {
+ case 400:
+ if (
+ "InvalidJson".equals(errorCode)
+ ) return new InvalidJsonException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ if (
+ "TooManyTerms".equals(errorCode)
+ ) return new TooManyTermsException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ if (
+ "TimeoutTooLarge".equals(errorCode)
+ ) return new TimeoutTooLargeException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ if (
+ "TimeoutExceeded".equals(errorCode)
+ ) return new TimeoutExceededException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ if (
+ "InvalidNumberOfStringsToGenerate".equals(errorCode)
+ ) return new InvalidNumberOfStringsToGenerateException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ return new BadRequestException(message, code, errorCode, body);
+ case 401:
+ if (
+ "MissingOrMalformedToken".equals(errorCode)
+ ) return new MissingOrMalformedTokenException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ if (
+ "InvalidToken".equals(errorCode)
+ ) return new InvalidTokenException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ return new UnauthorizedException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ case 403:
+ if (
+ "QuotaExceeded".equals(errorCode)
+ ) return new QuotaExceededException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ return new ForbiddenException(message, code, errorCode, body);
+ case 404:
+ return new NotFoundException(message, code, errorCode, body);
+ case 429:
+ return new TooManyRequestsException(
+ "Max retries exceeded for 429 Too Many Requests.",
+ code,
+ errorCode,
+ body
+ );
+ case 500:
+ return new InternalServerException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ default:
+ return new com.regexsolver.api.exceptions.ApiException(
+ message,
+ code,
+ errorCode,
+ body
+ );
+ }
+ }
+
+ // --- ANALYZE OPERATIONS ---
+
+ /**
+ * Computes how many unique strings the term matches asynchronously.
+ *
+ * @param term The term to analyze.
+ * @return A CompletableFuture containing a Cardinality object representing either an exact Integer, a BigInteger, or Infinite cardinality.
+ */
+ public CompletableFuture getCardinality(Term term) {
+ return getCardinality(term, null);
+ }
+
+ /**
+ * Computes how many unique strings the term matches asynchronously.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a Cardinality object representing either an exact Integer, a BigInteger, or Infinite cardinality.
+ */
+ public CompletableFuture getCardinality(
+ Term term,
+ Integer timeout
+ ) {
+ if (term.getCachedCardinality() != null) {
+ return CompletableFuture.completedFuture(
+ term.getCachedCardinality()
+ );
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() ->
+ analyzeApi.cardinality(request)
+ ).thenApply(resp -> {
+ Cardinality card = Cardinality.fromDto(resp.getData());
+ term.setCachedCardinality(card);
+ return card;
+ });
+ }
+
+ /**
+ * Computes the minimum and maximum length of strings matched by the term asynchronously.
+ *
+ * @param term The term to analyze.
+ * @return A CompletableFuture containing a Length object with `min` and `max` integers. Limits are null if unbounded or undefined.
+ */
+ public CompletableFuture getLength(Term term) {
+ return getLength(term, null);
+ }
+
+ /**
+ * Computes the minimum and maximum length of strings matched by the term asynchronously.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a Length object with `min` and `max` integers. Limits are null if unbounded or undefined.
+ */
+ public CompletableFuture getLength(Term term, Integer timeout) {
+ if (term.getCachedLength() != null) {
+ return CompletableFuture.completedFuture(term.getCachedLength());
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.length(request)).thenApply(
+ resp -> {
+ Length len = Length.fromDto(resp.getData());
+ term.setCachedLength(len);
+ return len;
+ }
+ );
+ }
+
+ /**
+ * Checks if the term matches no strings at all asynchronously.
+ *
+ * @param term The term to analyze.
+ * @return A CompletableFuture containing true if the language is completely empty, false otherwise.
+ */
+ public CompletableFuture isEmpty(Term term) {
+ return isEmpty(term, null);
+ }
+
+ /**
+ * Checks if the term matches no strings at all asynchronously.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing true if the language is completely empty, false otherwise.
+ */
+ public CompletableFuture isEmpty(Term term, Integer timeout) {
+ if (term.getCachedEmpty() != null) {
+ return CompletableFuture.completedFuture(term.getCachedEmpty());
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.empty(request)).thenApply(
+ resp -> {
+ boolean val = resp.getData().getValue();
+ term.setCachedEmpty(val);
+ if (val) {
+ term.setCachedCardinality(new Cardinality.Integer(0));
+ term.setCachedLength(new Length(null, null));
+ }
+ return val;
+ }
+ );
+ }
+
+ /**
+ * Checks if the term matches only the empty string asynchronously.
+ *
+ * @param term The term to analyze.
+ * @return A CompletableFuture containing true if the term strictly matches the empty string ("") and nothing else.
+ */
+ public CompletableFuture isEmptyString(Term term) {
+ return isEmptyString(term, null);
+ }
+
+ /**
+ * Checks if the term matches only the empty string asynchronously.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing true if the term strictly matches the empty string ("") and nothing else.
+ */
+ public CompletableFuture isEmptyString(
+ Term term,
+ Integer timeout
+ ) {
+ if (term.getCachedEmptyString() != null) {
+ return CompletableFuture.completedFuture(
+ term.getCachedEmptyString()
+ );
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() ->
+ analyzeApi.emptyString(request)
+ ).thenApply(resp -> {
+ boolean val = resp.getData().getValue();
+ term.setCachedEmptyString(val);
+
+ if (val) {
+ term.setCachedCardinality(new Cardinality.Integer(1));
+ term.setCachedLength(new Length(0, 0));
+ }
+ return val;
+ });
+ }
+
+ /**
+ * Checks if the term matches all possible strings asynchronously.
+ *
+ * @param term The term to analyze.
+ * @return A CompletableFuture containing true if the term matches every possible string.
+ */
+ public CompletableFuture isTotal(Term term) {
+ return isTotal(term, null);
+ }
+
+ /**
+ * Checks if the term matches all possible strings asynchronously.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing true if the term matches every possible string.
+ */
+ public CompletableFuture isTotal(Term term, Integer timeout) {
+ if (term.getCachedTotal() != null) {
+ return CompletableFuture.completedFuture(term.getCachedTotal());
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.total(request)).thenApply(
+ resp -> {
+ boolean val = resp.getData().getValue();
+ term.setCachedTotal(val);
+
+ if (val) {
+ term.setCachedCardinality(new Cardinality.Infinite());
+ term.setCachedLength(new Length(0, null));
+ }
+ return val;
+ }
+ );
+ }
+
+ /**
+ * Returns a regular expression pattern that represents the term asynchronously.
+ *
+ * @param term The term to extract the pattern from.
+ * @return A CompletableFuture containing a valid regular expression string representing the language.
+ */
+ public CompletableFuture getPattern(Term term) {
+ return getPattern(term, null);
+ }
+
+ /**
+ * Returns a regular expression pattern that represents the term asynchronously.
+ *
+ * @param term The term to extract the pattern from.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a valid regular expression string representing the language.
+ */
+ public CompletableFuture getPattern(Term term, Integer timeout) {
+ Optional patternOpt = term.getPattern();
+ if (patternOpt.isPresent()) {
+ return CompletableFuture.completedFuture(patternOpt.get());
+ }
+
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.pattern(request)).thenApply(
+ resp -> {
+ String val = resp.getData().getValue();
+ term.setCachedPattern(val);
+ return val;
+ }
+ );
+ }
+
+ /**
+ * Builds a Graphviz DOT representation of the term's automaton asynchronously.
+ *
+ * @param term The term to visualize.
+ * @return A CompletableFuture containing the raw DOT syntax for Graphviz compilation.
+ */
+ public CompletableFuture getDot(Term term) {
+ return getDot(term, null);
+ }
+
+ /**
+ * Builds a Graphviz DOT representation of the term's automaton asynchronously.
+ *
+ * @param term The term to visualize.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing the raw DOT syntax for Graphviz compilation.
+ */
+ public CompletableFuture getDot(Term term, Integer timeout) {
+ if (term.getCachedDot() != null) {
+ return CompletableFuture.completedFuture(term.getCachedDot());
+ }
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.dot(request)).thenApply(
+ resp -> {
+ String val = resp.getData().getValue();
+ term.setCachedDot(val);
+ return val;
+ }
+ );
+ }
+
+ /**
+ * Checks if the two terms accept exactly the same language asynchronously.
+ *
+ * @param term1 The first term.
+ * @param term2 The second term to compare against.
+ * @return A CompletableFuture containing true if they are entirely equivalent, false otherwise.
+ */
+ public CompletableFuture equivalent(Term term1, Term term2) {
+ return equivalent(term1, term2, null);
+ }
+
+ /**
+ * Checks if the two terms accept exactly the same language asynchronously.
+ *
+ * @param term1 The first term.
+ * @param term2 The second term to compare against.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing true if they are entirely equivalent, false otherwise.
+ */
+ public CompletableFuture equivalent(
+ Term term1,
+ Term term2,
+ Integer timeout
+ ) {
+ TwoTermsRequestDto request = new TwoTermsRequestDto()
+ .addTermsItem(term1.toDto())
+ .addTermsItem(term2.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.equivalent(request)).thenApply(
+ resp -> resp.getData().getValue()
+ );
+ }
+
+ /**
+ * Checks if the first term's language is a subset of the second term's language asynchronously.
+ *
+ * @param subset The term to test as the subset.
+ * @param superset The term representing the entire set space.
+ * @return A CompletableFuture containing true if every string matched by subset is also matched by superset.
+ */
+ public CompletableFuture subset(Term subset, Term superset) {
+ return subset(subset, superset, null);
+ }
+
+ /**
+ * Checks if the first term's language is a subset of the second term's language asynchronously.
+ *
+ * @param subset The term to test as the subset.
+ * @param superset The term representing the entire set space.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing true if every string matched by subset is also matched by superset.
+ */
+ public CompletableFuture subset(
+ Term subset,
+ Term superset,
+ Integer timeout
+ ) {
+ TwoTermsRequestDto request = new TwoTermsRequestDto()
+ .addTermsItem(subset.toDto())
+ .addTermsItem(superset.toDto())
+ .options(buildOptions(timeout, null));
+ return executeWithRetry(() -> analyzeApi.subset(request)).thenApply(
+ resp -> resp.getData().getValue()
+ );
+ }
+
+ // --- COMPUTE OPERATIONS ---
+
+ /**
+ * Concatenates the given terms sequentially asynchronously.
+ *
+ * @param terms A dynamic list of terms to concatenate in order.
+ * @return A CompletableFuture containing a newly computed concatenated term.
+ */
+ public CompletableFuture concat(List terms) {
+ return concat(terms, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Concatenates the given terms sequentially asynchronously.
+ *
+ * @param terms A dynamic list of terms to concatenate in order.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a newly computed concatenated term.
+ */
+ public CompletableFuture concat(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ MultiTermsRequestDto request = new MultiTermsRequestDto()
+ .terms(terms.stream().map(Term::toDto).collect(Collectors.toList()))
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() -> computeApi.concat(request)).thenApply(
+ resp -> Term.fromDto(resp.getData())
+ );
+ }
+
+ /**
+ * Computes the intersection of the given terms asynchronously.
+ *
+ * @param terms A dynamic list of terms to intersect.
+ * @return A CompletableFuture containing a term representing only strings matched by ALL provided terms.
+ */
+ public CompletableFuture intersection(List terms) {
+ return intersection(terms, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Computes the intersection of the given terms asynchronously.
+ *
+ * @param terms A dynamic list of terms to intersect.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a term representing only strings matched by ALL provided terms.
+ */
+ public CompletableFuture intersection(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ MultiTermsRequestDto request = new MultiTermsRequestDto()
+ .terms(terms.stream().map(Term::toDto).collect(Collectors.toList()))
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() ->
+ computeApi.intersection(request)
+ ).thenApply(resp -> Term.fromDto(resp.getData()));
+ }
+
+ /**
+ * Computes the union of the given terms asynchronously.
+ *
+ * @param terms A dynamic list of terms to combine.
+ * @return A CompletableFuture containing a term representing strings matched by ANY of the provided terms.
+ */
+ public CompletableFuture union(List terms) {
+ return union(terms, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Computes the union of the given terms asynchronously.
+ *
+ * @param terms A dynamic list of terms to combine.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a term representing strings matched by ANY of the provided terms.
+ */
+ public CompletableFuture union(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ MultiTermsRequestDto request = new MultiTermsRequestDto()
+ .terms(terms.stream().map(Term::toDto).collect(Collectors.toList()))
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() -> computeApi.union(request)).thenApply(
+ resp -> Term.fromDto(resp.getData())
+ );
+ }
+
+ /**
+ * Computes the difference between the two provided terms asynchronously.
+ *
+ * @param base The base language term to subtract from.
+ * @param excluded The term whose language should be removed from the base.
+ * @return A CompletableFuture containing a computed difference term.
+ */
+ public CompletableFuture difference(Term base, Term excluded) {
+ return difference(base, excluded, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Computes the difference between the two provided terms asynchronously.
+ *
+ * @param base The base language term to subtract from.
+ * @param excluded The term whose language should be removed from the base.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a computed difference term.
+ */
+ public CompletableFuture difference(
+ Term base,
+ Term excluded,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ TwoTermsRequestDto request = new TwoTermsRequestDto()
+ .addTermsItem(base.toDto())
+ .addTermsItem(excluded.toDto())
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() -> computeApi.difference(request)).thenApply(
+ resp -> Term.fromDto(resp.getData())
+ );
+ }
+
+ /**
+ * Computes the complement of the given term asynchronously.
+ *
+ * @param term The term to complement.
+ * @return A CompletableFuture containing the complemented term.
+ */
+ public CompletableFuture complement(Term term) {
+ return complement(term, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Computes the complement of the given term asynchronously.
+ *
+ * @param term The term to complement.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing the complemented term.
+ */
+ public CompletableFuture complement(
+ Term term,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ TermRequestDto request = new TermRequestDto()
+ .term(term.toDto())
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() -> computeApi.complement(request)).thenApply(
+ resp -> Term.fromDto(resp.getData())
+ );
+ }
+
+ /**
+ * Repeats a term between a minimum and maximum number of times asynchronously.
+ *
+ * @param term The term to repeat.
+ * @param min The inclusive lower bound of repetitions.
+ * @param max The inclusive upper bound. If null, repetitions are unbounded.
+ * @return A CompletableFuture containing a computed repeated term.
+ */
+ public CompletableFuture repeat(Term term, int min, Integer max) {
+ return repeat(term, min, max, ResponseFormat.ANY, null);
+ }
+
+ /**
+ * Repeats a term between a minimum and maximum number of times asynchronously.
+ *
+ * @param term The term to repeat.
+ * @param min The inclusive lower bound of repetitions.
+ * @param max The inclusive upper bound. If null, repetitions are unbounded.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a computed repeated term.
+ */
+ public CompletableFuture repeat(
+ Term term,
+ int min,
+ Integer max,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ RepeatRequestDto request = new RepeatRequestDto()
+ .term(term.toDto())
+ .min(min)
+ .max(max)
+ .options(buildOptions(timeout, format));
+ return executeWithRetry(() -> computeApi.repeat(request)).thenApply(
+ resp -> Term.fromDto(resp.getData())
+ );
+ }
+
+ // --- GENERATE OPERATIONS ---
+
+ /**
+ * Generates up to {@code limit} distinct strings matched by the term, skipping the first {@code offset} strings asynchronously.
+ *
+ * @param term The term to sample generated strings from.
+ * @param limit The maximum number of unique strings to return.
+ * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination.
+ * @return A CompletableFuture containing a list of strings that match the term.
+ */
+ public CompletableFuture> generateStrings(
+ Term term,
+ int limit,
+ int offset
+ ) {
+ return generateStrings(term, limit, offset, null);
+ }
+
+ /**
+ * Generates up to {@code limit} distinct strings matched by the term, skipping the first {@code offset} strings asynchronously.
+ *
+ * @param term The term to sample generated strings from.
+ * @param limit The maximum number of unique strings to return.
+ * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A CompletableFuture containing a list of strings that match the term.
+ */
+ public CompletableFuture> generateStrings(
+ Term term,
+ int limit,
+ int offset,
+ Integer timeout
+ ) {
+ Term termToUse =
+ term.getCachedStableTerm() != null
+ ? term.getCachedStableTerm()
+ : term;
+ boolean returnStableTerm = term.getCachedStableTerm() == null;
+
+ GenerateStringsRequestDto request = new GenerateStringsRequestDto()
+ .term(termToUse.toDto())
+ .limit(limit)
+ .offset(offset)
+ .returnStableTerm(returnStableTerm)
+ .options(buildOptions(timeout, null));
+
+ return executeWithRetry(() -> generateApi.strings(request)).thenApply(
+ resp -> {
+ GenerateStringsResponseDto data = resp.getData();
+ if (data.getTerm() != null) {
+ term.setCachedStableTerm(Term.fromDto(data.getTerm()));
+ }
+ return data.getStrings().getValue();
+ }
+ );
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/Cardinality.java b/src/main/java/com/regexsolver/api/Cardinality.java
new file mode 100644
index 0000000..3718012
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/Cardinality.java
@@ -0,0 +1,146 @@
+package com.regexsolver.api;
+
+import com.regexsolver.api.generated.model.CardinalityBigIntegerDto;
+import com.regexsolver.api.generated.model.CardinalityDto;
+import com.regexsolver.api.generated.model.CardinalityIntegerDto;
+import java.util.Objects;
+import java.util.Optional;
+
+/** Base class representing the number of unique strings matched by a term. */
+public abstract class Cardinality extends TermPropertiesMixin {
+
+ private Cardinality() {}
+
+ static Cardinality fromDto(CardinalityDto card) {
+ Object inst = card.getActualInstance();
+ if (inst instanceof CardinalityIntegerDto) {
+ return new Cardinality.Integer(
+ ((CardinalityIntegerDto) inst).getValue()
+ );
+ } else if (inst instanceof CardinalityBigIntegerDto) {
+ return new Cardinality.BigInteger();
+ } else {
+ return new Cardinality.Infinite();
+ }
+ }
+
+ /** Indicates that the set of matched strings is finite and exactly calculable.
+ * @param value The exact count of uniquely matched strings.
+ */
+ public static final class Integer extends Cardinality {
+
+ private final long value;
+
+ public Integer(long value) {
+ this.value = value;
+ }
+
+ public long getValue() {
+ return value;
+ }
+
+ @Override
+ public Optional isEmpty() {
+ return Optional.of(value == 0);
+ }
+
+ @Override
+ public Optional isEmptyString() {
+ if (this.value == 1) {
+ return Optional.empty();
+ }
+ return Optional.of(false);
+ }
+
+ @Override
+ public Optional isTotal() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("", this.value);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Integer integer = (Integer) o;
+ return value == integer.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+ }
+
+ /** Indicates that the set of matched strings is finite but too large to be returned as a standard integer. */
+ public static final class BigInteger extends Cardinality {
+
+ @Override
+ public Optional isEmpty() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public Optional isEmptyString() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public Optional isTotal() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public String toString() {
+ return "";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof BigInteger;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+ }
+
+ /** Indicates that the set of matched strings is infinite. */
+ public static final class Infinite extends Cardinality {
+
+ @Override
+ public Optional isEmpty() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public Optional isEmptyString() {
+ return Optional.of(false);
+ }
+
+ @Override
+ public Optional isTotal() {
+ return Optional.empty();
+ }
+
+ @Override
+ public String toString() {
+ return "";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof Infinite;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/Length.java b/src/main/java/com/regexsolver/api/Length.java
new file mode 100644
index 0000000..ba1d97a
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/Length.java
@@ -0,0 +1,76 @@
+package com.regexsolver.api;
+
+import com.regexsolver.api.generated.model.LengthDto;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Represents the minimum and maximum lengths of any string matched by the term.
+ */
+public final class Length extends TermPropertiesMixin {
+
+ private final Integer min;
+ private final Integer max;
+
+ public Length(Integer min, Integer max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ static Length fromDto(LengthDto len) {
+ return new Length(len.getMin(), len.getMax());
+ }
+
+ /** The shortest possible matched string length, or {@link Optional::empty} if the language is empty. */
+ public Optional getMin() {
+ return Optional.ofNullable(min);
+ }
+
+ /** The longest possible matched string length, or {@link Optional::empty} if the length is unbounded. */
+ public Optional getMax() {
+ return Optional.ofNullable(max);
+ }
+
+ @Override
+ public Optional isEmpty() {
+ return Optional.of(this.min == null && this.max == null);
+ }
+
+ @Override
+ public Optional isEmptyString() {
+ return Optional.of(this.min == 0 && this.max == 0);
+ }
+
+ @Override
+ public Optional isTotal() {
+ if (this.min != 0 || this.max != null) {
+ return Optional.of(false);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Length length = (Length) o;
+ return (
+ Objects.equals(min, length.min) && Objects.equals(max, length.max)
+ );
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(min, max);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "",
+ min == null ? "null" : min,
+ max == null ? "null" : max
+ );
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/OperationOptions.java b/src/main/java/com/regexsolver/api/OperationOptions.java
deleted file mode 100644
index 836e039..0000000
--- a/src/main/java/com/regexsolver/api/OperationOptions.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.regexsolver.api;
-
-public class OperationOptions {
- protected ResponseFormat responseFormat;
- protected Integer executionTimeout;
-
- public static OperationOptions newDefault() {
- return new OperationOptions();
- }
-
- public OperationOptions responseFormat(ResponseFormat responseFormat) {
- this.responseFormat = responseFormat;
- return this;
- }
-
- public ResponseFormat responseFormat() {
- return responseFormat;
- }
-
- public OperationOptions executionTimeout(Integer executionTimeout) {
- this.executionTimeout = executionTimeout;
- return this;
- }
-
- public Integer executionTimeout() {
- return executionTimeout;
- }
-}
diff --git a/src/main/java/com/regexsolver/api/RateLimiter.java b/src/main/java/com/regexsolver/api/RateLimiter.java
new file mode 100644
index 0000000..6ad1f24
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/RateLimiter.java
@@ -0,0 +1,54 @@
+package com.regexsolver.api;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Global rate limiter shared by apiToken.
+ */
+class RateLimiter {
+
+ private static final ConcurrentHashMap INSTANCES =
+ new ConcurrentHashMap<>();
+
+ private final AtomicReference retryAfter = new AtomicReference<>(
+ Instant.MIN
+ );
+
+ private RateLimiter() {}
+
+ public static RateLimiter getInstance(String apiToken) {
+ return INSTANCES.computeIfAbsent(apiToken, k -> new RateLimiter());
+ }
+
+ public CompletableFuture waitIfNecessary() {
+ Instant now = Instant.now();
+ Instant retryAt = retryAfter.get();
+
+ if (retryAt.isAfter(now)) {
+ long delay = Duration.between(now, retryAt).toMillis();
+ if (delay > 0) {
+ return CompletableFuture.runAsync(() -> {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ });
+ }
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+
+ public void trigger(double seconds) {
+ Instant nextRetry = Instant.now().plus(
+ Duration.ofMillis((long) (seconds * 1000))
+ );
+ retryAfter.updateAndGet(current ->
+ nextRetry.isAfter(current) ? nextRetry : current
+ );
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/RegexSolver.java b/src/main/java/com/regexsolver/api/RegexSolver.java
deleted file mode 100644
index 612b0de..0000000
--- a/src/main/java/com/regexsolver/api/RegexSolver.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.regexsolver.api;
-
-public final class RegexSolver {
- public static void initialize() {
- RegexSolverApiWrapper.initialize();
- }
-
- public static void initialize(String token) {
- RegexSolverApiWrapper.initialize(token);
- }
-
- public static void initialize(String token, String baseUrl) {
- RegexSolverApiWrapper.initialize(token, baseUrl);
- }
-}
diff --git a/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java b/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
deleted file mode 100644
index f2332af..0000000
--- a/src/main/java/com/regexsolver/api/RegexSolverApiWrapper.java
+++ /dev/null
@@ -1,280 +0,0 @@
-package com.regexsolver.api;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.regexsolver.api.Request.GenerateStringsRequest;
-import com.regexsolver.api.Request.MultiTermsRequest;
-import com.regexsolver.api.Request.RepeatRequest;
-import com.regexsolver.api.Response.BooleanResponse;
-import com.regexsolver.api.Response.StringResponse;
-import com.regexsolver.api.Response.StringsResponse;
-import com.regexsolver.api.dto.Cardinality;
-import com.regexsolver.api.dto.Length;
-import com.regexsolver.api.exception.ApiError;
-import com.regexsolver.api.exception.MissingAPITokenException;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.ResponseBody;
-import retrofit2.Call;
-import retrofit2.Response;
-import retrofit2.Retrofit;
-import retrofit2.converter.jackson.JacksonConverterFactory;
-import retrofit2.http.Body;
-import retrofit2.http.POST;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-final class RegexSolverApiWrapper {
- private static final RegexSolverApiWrapper INSTANCE = new RegexSolverApiWrapper();
-
- private final static String DEFAULT_BASE_URL = "https://api.regexsolver.com/v1/";
-
- private final static String USER_AGENT = "RegexSolver Java / 1.1.0";
-
- private RegexApi api;
-
- public static RegexSolverApiWrapper getInstance() {
- return INSTANCE;
- }
-
- private RegexSolverApiWrapper() {
- initializeInternal(null, DEFAULT_BASE_URL);
- }
-
- private static String getConfiguredBaseUrl() {
- return Optional.ofNullable(System.getenv("REGEXSOLVER_BASE_URL")).orElse(DEFAULT_BASE_URL);
- }
-
- static void initialize() {
- getInstance().initializeInternal(System.getenv("REGEXSOLVER_API_TOKEN"), getConfiguredBaseUrl());
- }
-
- static void initialize(String token) {
- getInstance().initializeInternal(token, getConfiguredBaseUrl());
- }
-
- static void initialize(String token, String baseUrl) {
- getInstance().initializeInternal(token, baseUrl);
- }
-
- private void initializeInternal(String token, String baseUrl) {
- Retrofit retrofit = new Retrofit.Builder()
- .client(new OkHttpClient.Builder().addInterceptor(chain -> {
- if (token == null) {
- throw new MissingAPITokenException();
- }
- Request newRequest = chain.request().newBuilder()
- .addHeader("User-Agent", USER_AGENT)
- .addHeader("Authorization", "Bearer " + token)
- .build();
- return chain.proceed(newRequest);
- }).build())
- .baseUrl(baseUrl)
- .addConverterFactory(JacksonConverterFactory.create())
- .build();
-
- api = retrofit.create(RegexApi.class);
- }
-
- // Analyze
-
- public Cardinality analyzeCardinality(Term term) throws ApiError, IOException {
- Response response = api.analyzeCardinality(term).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public String analyzeDot(Term term) throws ApiError, IOException {
- Response response = api.analyzeDot(term).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public boolean analyzeEquivalent(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.analyzeEquivalent(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public boolean analyzeEmpty(Term term) throws ApiError, IOException {
- Response response = api.analyzeEmpty(term).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public boolean analyzeEmptyString(Term term) throws ApiError, IOException {
- Response response = api.analyzeEmptyString(term).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public Length analyzeLength(Term term) throws ApiError, IOException {
- Response response = api.analyzeLength(term).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public String analyzePattern(Term term) throws ApiError, IOException {
- Response response = api.analyzePattern(term).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public boolean analyzeSubset(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.analyzeSubset(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- public boolean analyzeTotal(Term term) throws ApiError, IOException {
- Response response = api.analyzeTotal(term).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- // Compute
-
- public Term computeConcat(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.computeConcat(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public Term computeDifference(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.computeDifference(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public Term computeIntersection(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.computeIntersection(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public Term computeRepeat(RepeatRequest repeatRequest) throws ApiError, IOException {
- Response response = api.computeRepeat(repeatRequest).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- public Term computeUnion(MultiTermsRequest multiTermsRequest) throws ApiError, IOException {
- Response response = api.computeUnion(multiTermsRequest).execute();
- if (response.isSuccessful()) {
- return response.body();
- } else {
- throw getApiError(response);
- }
- }
-
- // Generate
-
- public List generateStrings(GenerateStringsRequest generateStringsRequest) throws ApiError, IOException {
- Response response = api.generateStrings(generateStringsRequest).execute();
- if (response.isSuccessful()) {
- return response.body().value();
- } else {
- throw getApiError(response);
- }
- }
-
- private static ApiError getApiError(Response response) throws IOException {
- assert !response.isSuccessful();
- try (ResponseBody errorBody = response.errorBody()) {
- String json = Objects.requireNonNull(errorBody).string();
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(json, ApiError.class);
- }
- }
-
- private interface RegexApi {
- // analyze
- @POST("analyze/cardinality")
- Call analyzeCardinality(@Body Term term);
-
- @POST("analyze/dot")
- Call analyzeDot(@Body Term term);
-
- @POST("analyze/equivalent")
- Call analyzeEquivalent(@Body MultiTermsRequest multiTermsRequest);
-
- @POST("analyze/empty")
- Call analyzeEmpty(@Body Term term);
-
- @POST("analyze/empty_string")
- Call analyzeEmptyString(@Body Term term);
-
- @POST("analyze/length")
- Call analyzeLength(@Body Term term);
-
- @POST("analyze/pattern")
- Call analyzePattern(@Body Term term);
-
- @POST("analyze/subset")
- Call analyzeSubset(@Body MultiTermsRequest multiTermsRequest);
-
- @POST("analyze/total")
- Call analyzeTotal(@Body Term term);
-
- // compute
- @POST("compute/concat")
- Call computeConcat(@Body MultiTermsRequest multiTermsRequest);
-
- @POST("compute/difference")
- Call computeDifference(@Body MultiTermsRequest multiTermsRequest);
-
- @POST("compute/intersection")
- Call computeIntersection(@Body MultiTermsRequest multiTermsRequest);
-
- @POST("compute/repeat")
- Call computeRepeat(@Body RepeatRequest repeatRequest);
-
- @POST("compute/union")
- Call computeUnion(@Body MultiTermsRequest multiTermsRequest);
-
- // generate
- @POST("generate/strings")
- Call generateStrings(@Body GenerateStringsRequest request);
- }
-}
diff --git a/src/main/java/com/regexsolver/api/RegexSolverClient.java b/src/main/java/com/regexsolver/api/RegexSolverClient.java
new file mode 100644
index 0000000..9191f3e
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/RegexSolverClient.java
@@ -0,0 +1,431 @@
+package com.regexsolver.api;
+
+import java.util.List;
+
+/**
+ * The Synchronous Client for RegexSolver.
+ *
+ * Provides blocking access to all RegexSolver API endpoints.
+ */
+public final class RegexSolverClient {
+
+ private final AsyncRegexSolverClient asyncClient;
+
+ private RegexSolverClient(AsyncRegexSolverClient asyncClient) {
+ this.asyncClient = asyncClient;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+
+ private final AsyncRegexSolverClient.Builder asyncBuilder =
+ AsyncRegexSolverClient.builder();
+
+ public Builder apiToken(String apiToken) {
+ asyncBuilder.apiToken(apiToken);
+ return this;
+ }
+
+ public Builder baseUrl(String baseUrl) {
+ asyncBuilder.baseUrl(baseUrl);
+ return this;
+ }
+
+ public RegexSolverClient build() {
+ return new RegexSolverClient(asyncBuilder.build());
+ }
+ }
+
+ // --- ANALYZE OPERATIONS ---
+
+ /**
+ * Computes how many unique strings the term matches.
+ *
+ * @param term The term to analyze.
+ * @return A Cardinality object representing either an exact Integer, a BigInteger, or Infinite cardinality.
+ */
+ public Cardinality getCardinality(Term term) {
+ return asyncClient.getCardinality(term).join();
+ }
+
+ /**
+ * Computes how many unique strings the term matches, with a timeout.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A Cardinality object representing either an exact Integer, a BigInteger, or Infinite cardinality.
+ */
+ public Cardinality getCardinality(Term term, Integer timeout) {
+ return asyncClient.getCardinality(term, timeout).join();
+ }
+
+ /**
+ * Computes the minimum and maximum length of strings matched by the term.
+ *
+ * @param term The term to analyze.
+ * @return A Length object containing min and max integers. Limits are null if unbounded or undefined.
+ */
+ public Length getLength(Term term) {
+ return asyncClient.getLength(term).join();
+ }
+
+ /**
+ * Computes the minimum and maximum length of strings matched by the term, with a timeout.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A Length object containing min and max integers. Limits are null if unbounded or undefined.
+ */
+ public Length getLength(Term term, Integer timeout) {
+ return asyncClient.getLength(term, timeout).join();
+ }
+
+ /**
+ * Checks if the term matches no strings at all.
+ *
+ * @param term The term to analyze.
+ * @return True if the language is completely empty, false otherwise.
+ */
+ public boolean isEmpty(Term term) {
+ return asyncClient.isEmpty(term).join();
+ }
+
+ /**
+ * Checks if the term matches no strings at all, with a timeout.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return True if the language is completely empty, false otherwise.
+ */
+ public boolean isEmpty(Term term, Integer timeout) {
+ return asyncClient.isEmpty(term, timeout).join();
+ }
+
+ /**
+ * Checks if the term matches only the empty string.
+ *
+ * @param term The term to analyze.
+ * @return True if the term strictly matches the empty string ("") and nothing else.
+ */
+ public boolean isEmptyString(Term term) {
+ return asyncClient.isEmptyString(term).join();
+ }
+
+ /**
+ * Checks if the term matches only the empty string, with a timeout.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return True if the term strictly matches the empty string ("") and nothing else.
+ */
+ public boolean isEmptyString(Term term, Integer timeout) {
+ return asyncClient.isEmptyString(term, timeout).join();
+ }
+
+ /**
+ * Checks if the term matches all possible strings.
+ *
+ * @param term The term to analyze.
+ * @return True if the term matches every possible string.
+ */
+ public boolean isTotal(Term term) {
+ return asyncClient.isTotal(term).join();
+ }
+
+ /**
+ * Checks if the term matches all possible strings, with a timeout.
+ *
+ * @param term The term to analyze.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return True if the term matches every possible string.
+ */
+ public boolean isTotal(Term term, Integer timeout) {
+ return asyncClient.isTotal(term, timeout).join();
+ }
+
+ /**
+ * Returns a regular expression pattern that represents the term.
+ *
+ * @param term The term to extract the pattern from.
+ * @return A valid regular expression string representing the language.
+ */
+ public String getPattern(Term term) {
+ return asyncClient.getPattern(term).join();
+ }
+
+ /**
+ * Returns a regular expression pattern that represents the term, with a timeout.
+ *
+ * @param term The term to extract the pattern from.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A valid regular expression string representing the language.
+ */
+ public String getPattern(Term term, Integer timeout) {
+ return asyncClient.getPattern(term, timeout).join();
+ }
+
+ /**
+ * Builds a Graphviz DOT representation of the term's automaton.
+ *
+ * @param term The term to visualize.
+ * @return The raw DOT syntax for Graphviz compilation.
+ */
+ public String getDot(Term term) {
+ return asyncClient.getDot(term).join();
+ }
+
+ /**
+ * Builds a Graphviz DOT representation of the term's automaton, with a timeout.
+ *
+ * @param term The term to visualize.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return The raw DOT syntax for Graphviz compilation.
+ */
+ public String getDot(Term term, Integer timeout) {
+ return asyncClient.getDot(term, timeout).join();
+ }
+
+ /**
+ * Checks if the two terms accept exactly the same language.
+ *
+ * @param term1 The first term.
+ * @param term2 The second term to compare against.
+ * @return True if they are entirely equivalent, false otherwise.
+ */
+ public boolean equivalent(Term term1, Term term2) {
+ return asyncClient.equivalent(term1, term2).join();
+ }
+
+ /**
+ * Checks if the two terms accept exactly the same language, with a timeout.
+ *
+ * @param term1 The first term.
+ * @param term2 The second term to compare against.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return True if they are entirely equivalent, false otherwise.
+ */
+ public boolean equivalent(Term term1, Term term2, Integer timeout) {
+ return asyncClient.equivalent(term1, term2, timeout).join();
+ }
+
+ /**
+ * Checks if the first term's language is a subset of the second term's language.
+ *
+ * @param subset The term to test as the subset.
+ * @param superset The term representing the entire set space.
+ * @return True if every string matched by subset is also matched by superset.
+ */
+ public boolean subset(Term subset, Term superset) {
+ return asyncClient.subset(subset, superset).join();
+ }
+
+ /**
+ * Checks if the first term's language is a subset of the second term's language, with a timeout.
+ *
+ * @param subset The term to test as the subset.
+ * @param superset The term representing the entire set space.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return True if every string matched by subset is also matched by superset.
+ */
+ public boolean subset(Term subset, Term superset, Integer timeout) {
+ return asyncClient.subset(subset, superset, timeout).join();
+ }
+
+ // --- COMPUTE OPERATIONS ---
+
+ /**
+ * Concatenates the given terms sequentially.
+ *
+ * @param terms A list of terms to concatenate in order.
+ * @return A newly computed concatenated term.
+ */
+ public Term concat(List terms) {
+ return asyncClient.concat(terms).join();
+ }
+
+ /**
+ * Concatenates the given terms sequentially, allowing for format and timeout specification.
+ *
+ * @param terms A list of terms to concatenate in order.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A newly computed concatenated term.
+ */
+ public Term concat(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ return asyncClient.concat(terms, format, timeout).join();
+ }
+
+ /**
+ * Computes the intersection of the given terms.
+ *
+ * @param terms A list of terms to intersect.
+ * @return A term representing only strings matched by ALL provided terms.
+ */
+ public Term intersection(List terms) {
+ return asyncClient.intersection(terms).join();
+ }
+
+ /**
+ * Computes the intersection of the given terms, allowing for format and timeout specification.
+ *
+ * @param terms A list of terms to intersect.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A term representing only strings matched by ALL provided terms.
+ */
+ public Term intersection(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ return asyncClient.intersection(terms, format, timeout).join();
+ }
+
+ /**
+ * Computes the union of the given terms.
+ *
+ * @param terms A list of terms to combine.
+ * @return A term representing strings matched by ANY of the provided terms.
+ */
+ public Term union(List terms) {
+ return asyncClient.union(terms).join();
+ }
+
+ /**
+ * Computes the union of the given terms, allowing for format and timeout specification.
+ *
+ * @param terms A list of terms to combine.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A term representing strings matched by ANY of the provided terms.
+ */
+ public Term union(
+ List terms,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ return asyncClient.union(terms, format, timeout).join();
+ }
+
+ /**
+ * Computes the difference between the two provided terms.
+ *
+ * @param base The base language term to subtract from.
+ * @param excluded The term whose language should be removed from the base.
+ * @return A computed difference term.
+ */
+ public Term difference(Term base, Term excluded) {
+ return asyncClient.difference(base, excluded).join();
+ }
+
+ /**
+ * Computes the difference between the two provided terms, allowing for format and timeout specification.
+ *
+ * @param base The base language term to subtract from.
+ * @param excluded The term whose language should be removed from the base.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A computed difference term.
+ */
+ public Term difference(
+ Term base,
+ Term excluded,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ return asyncClient.difference(base, excluded, format, timeout).join();
+ }
+
+ /**
+ * Computes the complement of the given term.
+ *
+ * @param term The term to complement.
+ * @return The complemented term.
+ */
+ public Term complement(Term term) {
+ return asyncClient.complement(term).join();
+ }
+
+ /**
+ * Computes the complement of the given term, allowing for format and timeout specification.
+ *
+ * @param term The term to complement.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return The complemented term.
+ */
+ public Term complement(Term term, ResponseFormat format, Integer timeout) {
+ return asyncClient.complement(term, format, timeout).join();
+ }
+
+ /**
+ * Repeats a term between a minimum and maximum number of times.
+ *
+ * @param term The term to repeat.
+ * @param min The inclusive lower bound of repetitions.
+ * @param max The inclusive upper bound. If null, repetitions are unbounded.
+ * @return A computed repeated term.
+ */
+ public Term repeat(Term term, int min, Integer max) {
+ return asyncClient.repeat(term, min, max).join();
+ }
+
+ /**
+ * Repeats a term between a minimum and maximum number of times, allowing for format and timeout specification.
+ *
+ * @param term The term to repeat.
+ * @param min The inclusive lower bound of repetitions.
+ * @param max The inclusive upper bound. If null, repetitions are unbounded.
+ * @param format The return format of the term (any, regex or fair).
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A computed repeated term.
+ */
+ public Term repeat(
+ Term term,
+ int min,
+ Integer max,
+ ResponseFormat format,
+ Integer timeout
+ ) {
+ return asyncClient.repeat(term, min, max, format, timeout).join();
+ }
+
+ // --- GENERATE OPERATIONS ---
+
+ /**
+ * Generates up to {@code limit} distinct strings matched by the term, skipping the first {@code offset} strings.
+ *
+ * @param term The term to sample generated strings from.
+ * @param limit The maximum number of unique strings to return.
+ * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination.
+ * @return A list of strings that match the term.
+ */
+ public List generateStrings(Term term, int limit, int offset) {
+ return asyncClient.generateStrings(term, limit, offset).join();
+ }
+
+ /**
+ * Generates up to {@code limit} distinct strings matched by the term, skipping the first {@code offset} strings, with a timeout.
+ *
+ * @param term The term to sample generated strings from.
+ * @param limit The maximum number of unique strings to return.
+ * @param offset Number of matched strings to skip before starting to collect the results. Used for pagination.
+ * @param timeout Timeout in milliseconds for the operation.
+ * @return A list of strings that match the term.
+ */
+ public List generateStrings(
+ Term term,
+ int limit,
+ int offset,
+ Integer timeout
+ ) {
+ return asyncClient.generateStrings(term, limit, offset, timeout).join();
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/Request.java b/src/main/java/com/regexsolver/api/Request.java
deleted file mode 100644
index 1aedbf4..0000000
--- a/src/main/java/com/regexsolver/api/Request.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package com.regexsolver.api;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.List;
-
-final class Request {
- @JsonInclude(JsonInclude.Include.NON_NULL)
- static final class RequestOptions {
- private final ResponseOptions response;
- private final ExecutionOptions execution;
-
- public static RequestOptions fromArgs(
- ResponseFormat format,
- Integer timeout) {
-
- ResponseOptions response = null;
- if (format != null) {
- response = new ResponseOptions(format);
- }
-
- ExecutionOptions execution = null;
- if (timeout != null) {
- execution = new ExecutionOptions(timeout);
- }
-
- if (response == null && execution == null) {
- return null;
- } else {
- return new RequestOptions(response, execution);
- }
- }
-
- public RequestOptions(
- @JsonProperty("response") ResponseOptions response,
- @JsonProperty("execution") ExecutionOptions execution) {
- this.response = response;
- this.execution = execution;
- }
-
- public int getSchemaVersion() {
- return 1;
- }
-
- public ResponseOptions getResponse() {
- return response;
- }
-
- public ExecutionOptions getExecution() {
- return execution;
- }
-
- public static final class ResponseOptions {
- private final ResponseFormat format;
-
- public ResponseOptions(@JsonProperty("format") ResponseFormat format) {
- this.format = format;
- }
-
- public ResponseFormat getFormat() {
- return format;
- }
- }
-
- public static final class ExecutionOptions {
- private final Integer timeout;
-
- public ExecutionOptions(@JsonProperty("timeout") Integer timeout) {
- this.timeout = timeout;
- }
-
- public Integer getTimeout() {
- return timeout;
- }
- }
- }
-
- @JsonInclude(JsonInclude.Include.NON_NULL)
- static final class MultiTermsRequest {
- private final List terms;
- private final RequestOptions options;
-
- public MultiTermsRequest(@JsonProperty("terms") List terms,
- @JsonProperty("options") RequestOptions options) {
- this.terms = terms;
- this.options = options;
- }
-
- public List getTerms() {
- return terms;
- }
-
- public RequestOptions getOptions() {
- return options;
- }
- }
-
- @JsonInclude(JsonInclude.Include.NON_NULL)
- static final class RepeatRequest {
- private final Term term;
- private final int min;
- private final Integer max;
- private final RequestOptions options;
-
- public RepeatRequest(
- @JsonProperty("term") Term term,
- @JsonProperty("min") int min,
- @JsonProperty("max") Integer max,
- @JsonProperty("options") RequestOptions options) {
- this.term = term;
- this.min = min;
- this.max = max;
- this.options = options;
- }
-
- public Term getTerm() {
- return term;
- }
-
- public int getMin() {
- return min;
- }
-
- public Integer getMax() {
- return max;
- }
-
- public RequestOptions getOptions() {
- return options;
- }
- }
-
- @JsonInclude(JsonInclude.Include.NON_NULL)
- static final class GenerateStringsRequest {
- private final Term term;
- private final int count;
- private final RequestOptions options;
-
- public GenerateStringsRequest(
- @JsonProperty("term") Term term,
- @JsonProperty("count") int count,
- @JsonProperty("options") RequestOptions options) {
- this.term = term;
- this.count = count;
- this.options = options;
- }
-
- public Term getTerm() {
- return term;
- }
-
- public int getCount() {
- return count;
- }
-
- public RequestOptions getOptions() {
- return options;
- }
- }
-}
diff --git a/src/main/java/com/regexsolver/api/Response.java b/src/main/java/com/regexsolver/api/Response.java
deleted file mode 100644
index 27ed766..0000000
--- a/src/main/java/com/regexsolver/api/Response.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.regexsolver.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.List;
-
-final class Response {
- public static final class BooleanResponse implements ResponseContent {
- private final boolean value;
-
- public BooleanResponse(@JsonProperty("value") boolean value) {
- this.value = value;
- }
-
- public boolean value() {
- return value;
- }
- }
-
- public static final class StringResponse implements ResponseContent {
- private final String value;
-
- public StringResponse(@JsonProperty("value") String value) {
- this.value = value;
- }
-
- public String value() {
- return value;
- }
- }
-
- public static final class StringsResponse implements ResponseContent {
- private final List value;
-
- public StringsResponse(@JsonProperty("value") List value) {
- this.value = value;
- }
-
- public List value() {
- return value;
- }
- }
-}
diff --git a/src/main/java/com/regexsolver/api/ResponseContent.java b/src/main/java/com/regexsolver/api/ResponseContent.java
deleted file mode 100644
index 5b4f260..0000000
--- a/src/main/java/com/regexsolver/api/ResponseContent.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.regexsolver.api;
-
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.regexsolver.api.Response.BooleanResponse;
-import com.regexsolver.api.Response.StringResponse;
-import com.regexsolver.api.Response.StringsResponse;
-
-@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
-@JsonSubTypes({
- @JsonSubTypes.Type(value = Term.Fair.class, name = "fair"),
- @JsonSubTypes.Type(value = Term.Regex.class, name = "regex"),
- @JsonSubTypes.Type(value = StringResponse.class, name = "string"),
- @JsonSubTypes.Type(value = StringsResponse.class, name = "strings"),
- @JsonSubTypes.Type(value = BooleanResponse.class, name = "boolean"),
-})
-public interface ResponseContent {
-}
diff --git a/src/main/java/com/regexsolver/api/ResponseFormat.java b/src/main/java/com/regexsolver/api/ResponseFormat.java
index a1aeee5..6398689 100644
--- a/src/main/java/com/regexsolver/api/ResponseFormat.java
+++ b/src/main/java/com/regexsolver/api/ResponseFormat.java
@@ -1,12 +1,27 @@
package com.regexsolver.api;
-import com.fasterxml.jackson.annotation.JsonProperty;
+import com.regexsolver.api.generated.model.ResponseOptionsDto.FormatEnum;
+/**
+ * Used in compute operations to specify the format of the result.
+ */
public enum ResponseFormat {
- @JsonProperty("any")
ANY,
- @JsonProperty("regex")
REGEX,
- @JsonProperty("fair")
- FAIR
+ FAIR;
+
+ public FormatEnum toDto() {
+ switch (this) {
+ case ANY:
+ return FormatEnum.ANY;
+ case REGEX:
+ return FormatEnum.REGEX;
+ case FAIR:
+ return FormatEnum.FAIR;
+ default:
+ throw new IllegalArgumentException(
+ String.format("Unsupported ResponseFormat %s.", this)
+ );
+ }
+ }
}
diff --git a/src/main/java/com/regexsolver/api/Term.java b/src/main/java/com/regexsolver/api/Term.java
index 433f471..665540d 100644
--- a/src/main/java/com/regexsolver/api/Term.java
+++ b/src/main/java/com/regexsolver/api/Term.java
@@ -1,548 +1,184 @@
package com.regexsolver.api;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.regexsolver.api.Request.GenerateStringsRequest;
-import com.regexsolver.api.Request.MultiTermsRequest;
-import com.regexsolver.api.Request.RepeatRequest;
-import com.regexsolver.api.Request.RequestOptions;
-import com.regexsolver.api.dto.Cardinality;
-import com.regexsolver.api.dto.Length;
-import com.regexsolver.api.exception.ApiError;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import com.regexsolver.api.generated.model.TermDto;
+import com.regexsolver.api.generated.model.TermFairDto;
+import com.regexsolver.api.generated.model.TermRegexDto;
import java.util.Objects;
import java.util.Optional;
+import java.util.regex.Pattern;
/**
- * This abstract class represents a term on which it is possible to perform
- * operations.
+ * Represents a mathematical term (Regex or FAIR) on which operations can be performed.
*/
-public abstract class Term implements ResponseContent {
- @JsonIgnore
- private final static String REGEX_PREFIX = "regex";
- @JsonIgnore
- private final static String FAIR_PREFIX = "fair";
- @JsonIgnore
- private final static String UNKNOWN_PREFIX = "unknown";
+public abstract class Term {
private final String value;
- @JsonIgnore
- private transient String serialized = null;
-
- @JsonIgnore
- private transient Cardinality cardinality;
- @JsonIgnore
- private transient Length length;
- @JsonIgnore
- private transient Boolean empty;
- @JsonIgnore
- private transient Boolean total;
- @JsonIgnore
- private transient Boolean emptyString;
- @JsonIgnore
- private transient String pattern;
- @JsonIgnore
- private transient String dot;
+ // Shared Cache (Internal)
+ private Cardinality cardinality;
+ private Length length;
+ private Boolean empty;
+ private Boolean emptyString;
+ private Boolean total;
+ protected String pattern;
+ private String dot;
+ private Term stableTerm;
+
+ private Pattern compiledRegex;
- /**
- * Create a new instance.
- *
- * @param value The value of the term.
- */
protected Term(String value) {
this.value = value;
}
- /**
- * Create a new instance of {@link Term.Regex}.
- *
- * @param regex The regular expression pattern.
- * @return The created instance.
- */
- public static Term.Regex regex(String regex) {
- return new Term.Regex(regex);
- }
+ public abstract Optional getPattern();
- /**
- * Create a new instance of {@link Term.Fair}.
- *
- * @param fair The FAIR.
- * @return The created instance.
- */
- public static Term.Fair fair(String fair) {
- return new Term.Fair(fair);
- }
+ public abstract Optional getFair();
- public String getValue() {
- return value;
- }
+ abstract TermDto toDto();
- private static RequestOptions loadRequestOptions(OperationOptions opts) {
- RequestOptions requestOptions = null;
- if (opts != null) {
- requestOptions = RequestOptions.fromArgs(opts.responseFormat(), opts.executionTimeout());
- }
- return requestOptions;
+ public static Term regex(String pattern) {
+ return new RegexTerm(pattern);
}
- // Analyze
-
- /**
- * Check equivalence with the given term.
- *
- * @param opts Execution options.
- * @param term The term to check equivalence with.
- * @return true if the terms are equivalent, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean equivalent(OperationOptions opts, Term term) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .analyzeEquivalent(new MultiTermsRequest(getArgs(term),
- loadRequestOptions(opts)));
+ public static Term fair(String payload) {
+ return new FairTerm(payload);
}
- /**
- * Check equivalence with the given term.
- *
- * @param term The term to check equivalence with.
- * @return true if the terms are equivalent, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean equivalent(Term term) throws IOException, ApiError {
- return equivalent(null, term);
- }
-
- /**
- * Get the cardinality of this term.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return A `Cardinality` object describing how many distinct strings are
- * matched.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Cardinality getCardinality() throws IOException, ApiError {
- if (cardinality != null) {
- return cardinality;
- }
- cardinality = RegexSolverApiWrapper.getInstance()
- .analyzeCardinality(this);
- return cardinality;
- }
+ // --- Shared Behavior ---
- /**
- * Get the GraphViz DOT representation of this term.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return A DOT language string describing the automaton for this term.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public String getDot() throws IOException, ApiError {
- if (dot != null) {
- return dot;
- }
- dot = RegexSolverApiWrapper.getInstance()
- .analyzeDot(this);
- return dot;
+ public String getValue() {
+ return value;
}
- /**
- * Return the Fast Automaton Internal Representation (FAIR).
- *
- * @return The FAIR.
- */
- @JsonIgnore
- public String getFair() throws IOException, ApiError {
- return null;
+ void setPropertiesMixin(TermPropertiesMixin propertiesMixin) {
+ propertiesMixin.isEmpty().ifPresent(this::setCachedEmpty);
+ propertiesMixin.isEmptyString().ifPresent(this::setCachedEmptyString);
+ propertiesMixin.isTotal().ifPresent(this::setCachedTotal);
}
/**
- * Get the length bounds of this term.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return A `Length` object with the minimum and maximum string length matched
- * by this term.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
+ * Client-side matching implementation.
+ * @param str The string to test against the term.
+ * @return True if matches, false if not. Throws if pattern is not set.
*/
- @JsonIgnore
- public Length getLength() throws IOException, ApiError {
- if (length != null) {
- return length;
+ public boolean isMatch(String str) {
+ Optional patternOpt = getPattern();
+ if (patternOpt.isEmpty()) {
+ throw new IllegalStateException(
+ "The regex pattern of this term is not defined yet, call getPattern() on the client to set it."
+ );
}
- length = RegexSolverApiWrapper.getInstance()
- .analyzeLength(this);
- return length;
- }
-
- /**
- * Return the regular expression pattern.
- *
- * If the term is not a regex the pattern will be resolved.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return The regular expression pattern.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public String getPattern() throws IOException, ApiError {
- if (pattern != null) {
- return pattern;
+ if (compiledRegex == null) {
+ compiledRegex = Pattern.compile(patternOpt.get(), Pattern.DOTALL);
}
- pattern = RegexSolverApiWrapper.getInstance()
- .analyzePattern(this);
- return pattern;
+ return compiledRegex.matcher(str).matches();
}
- /**
- * Check whether this term matches no string.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return true if the term is empty, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean isEmpty() throws IOException, ApiError {
- if (empty != null) {
- return empty;
- }
-
- empty = RegexSolverApiWrapper.getInstance()
- .analyzeEmpty(this);
- return empty;
- }
+ public abstract String serialize();
- /**
- * Check whether this term matches only the empty string.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return true if the term only matches the empty string, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean isEmptyString() throws IOException, ApiError {
- if (emptyString != null) {
- return emptyString;
+ public static Optional deserialize(String serialized) {
+ if (serialized == null || !serialized.contains("=")) {
+ return Optional.empty();
}
- emptyString = RegexSolverApiWrapper.getInstance()
- .analyzeEmptyString(this);
- return emptyString;
- }
+ int index = serialized.indexOf("=");
+ String typeStr = serialized.substring(0, index);
+ String val = serialized.substring(index + 1);
- /**
- * Check whether this term matches all possible strings.
- * Cache the result to avoid calling the API again if this method is called
- * multiple times.
- *
- * @return true if the term matches all possible strings, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean isTotal() throws IOException, ApiError {
- if (total != null) {
- return total;
+ if ("regex".equalsIgnoreCase(typeStr)) {
+ return Optional.of(regex(val));
+ } else if ("fair".equalsIgnoreCase(typeStr)) {
+ return Optional.of(fair(val));
}
-
- total = RegexSolverApiWrapper.getInstance()
- .analyzeTotal(this);
- return total;
- }
-
- /**
- * Check if is a subset of the given term.
- *
- * @param opts Execution options.
- * @param term The term to check if is the superset of this.
- * @return true if this is a subset, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean subset(OperationOptions opts, Term term) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .analyzeSubset(new MultiTermsRequest(getArgs(term), loadRequestOptions(opts)));
+ return Optional.empty();
}
- /**
- * Check if is a subset of the given term.
- *
- * @param term The term to check if is the superset of this.
- * @return true if this is a subset, false otherwise.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public boolean subset(Term term) throws IOException, ApiError {
- return subset(null, term);
+ static Term fromDto(TermDto dto) {
+ Object instance = dto.getActualInstance();
+ if (instance instanceof TermRegexDto) {
+ return Term.regex(((TermRegexDto) instance).getValue());
+ } else {
+ return Term.fair(((TermFairDto) instance).getValue());
+ }
}
- // Compute
+ // --- Shared Getters/Setters ---
- @JsonIgnore
- private List getArgs(Term... terms) {
- ArrayList args = new ArrayList<>();
- args.add(this);
- args.addAll(List.of(terms));
- return args;
+ Cardinality getCachedCardinality() {
+ return cardinality;
}
- /**
- * Compute the concat with the given terms and return the resulting term.
- *
- * @param opts Execution options.
- * @param terms The terms to compute an concat with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term concat(OperationOptions opts, Term... terms) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .computeConcat(new MultiTermsRequest(getArgs(terms), loadRequestOptions(opts)));
+ void setCachedCardinality(Cardinality cardinality) {
+ setPropertiesMixin(cardinality);
+ this.cardinality = cardinality;
}
- /**
- * Compute the concat with the given terms and return the resulting term.
- *
- * @param terms The terms to compute an concat with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term concat(Term... terms) throws IOException, ApiError {
- return concat(null, terms);
+ Length getCachedLength() {
+ return length;
}
- /**
- * Compute the difference with the given term and return the resulting term.
- *
- * @param opts Execution options.
- * @param term The term to subtract.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term difference(OperationOptions opts, Term term) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .computeDifference(new MultiTermsRequest(getArgs(term), loadRequestOptions(opts)));
+ void setCachedLength(Length length) {
+ setPropertiesMixin(length);
+ this.length = length;
}
- /**
- * Compute the difference with the given term and return the resulting term.
- *
- * @param term The term to subtract.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term difference(Term term) throws IOException, ApiError {
- return difference(null, term);
+ Boolean getCachedEmpty() {
+ return empty;
}
- /**
- * Compute the intersection with the given terms and return the resulting term.
- *
- * @param opts Execution options.
- * @param terms The terms to compute an intersection with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term intersection(OperationOptions opts, Term... terms) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .computeIntersection(new MultiTermsRequest(getArgs(terms), loadRequestOptions(opts)));
+ void setCachedEmpty(Boolean empty) {
+ this.empty = empty;
}
- /**
- * Compute the intersection with the given terms and return the resulting term.
- *
- * @param terms The terms to compute an intersection with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term intersection(Term... terms) throws IOException, ApiError {
- return intersection(null, terms);
+ Boolean getCachedEmptyString() {
+ return emptyString;
}
- /**
- * Computes the repetition of the term between `min` and `max` times; if `max`
- * is `null`, the repetition is unbounded.
- *
- * @param opts Execution options.
- * @param min The lower bound of the repetition.
- * @param max The upper bound of the repetition, if `null` the repetition is
- * unbounded.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term repeat(OperationOptions opts, int min, Integer max) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .computeRepeat(new RepeatRequest(this, min, max, loadRequestOptions(opts)));
+ void setCachedEmptyString(Boolean emptyString) {
+ this.emptyString = emptyString;
}
- /**
- * Computes the repetition of the term between `min` and `max` times; if `max`
- * is `null`, the repetition is unbounded.
- *
- * @param min The lower bound of the repetition.
- * @param max The upper bound of the repetition, if `null` the repetition is
- * unbounded.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term repeat(int min, Integer max) throws IOException, ApiError {
- return repeat(null, min, max);
+ Boolean getCachedTotal() {
+ return total;
}
- /**
- * Compute the union with the given terms and return the resulting term.
- *
- * @param opts Execution options.
- * @param terms The terms to compute a union with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term union(OperationOptions opts, Term... terms) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .computeUnion(new MultiTermsRequest(getArgs(terms), loadRequestOptions(opts)));
+ void setCachedTotal(Boolean total) {
+ this.total = total;
}
- /**
- * Compute the union with the given terms and return the resulting term.
- *
- * @param terms The terms to compute a union with.
- * @return The resulting term
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public Term union(Term... terms) throws IOException, ApiError {
- return union(null, terms);
+ void setCachedPattern(String pattern) {
+ this.pattern = pattern;
}
- // Generate
-
- /**
- * Generate the given number of unique strings matched by this term.
- *
- * @param opts Execution options.
- * @param count The number of unique strings to generate.
- * @return A list of unique strings matched by this term.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public List generateStrings(OperationOptions opts, int count) throws IOException, ApiError {
- return RegexSolverApiWrapper.getInstance()
- .generateStrings(new GenerateStringsRequest(this, count, loadRequestOptions(opts)));
+ String getCachedDot() {
+ return dot;
}
- /**
- * Generate the given number of unique strings matched by this term.
- *
- * @param count The number of unique strings to generate.
- * @return A list of unique strings matched by this term.
- * @throws IOException In case of issues requesting the API server.
- * @throws ApiError In case of error returned by the API.
- */
- @JsonIgnore
- public List generateStrings(int count) throws IOException, ApiError {
- return generateStrings(null, count);
+ void setCachedDot(String dot) {
+ this.dot = dot;
}
- /**
- * Generate a string representation that can be parsed by
- * {@link #deserialize(String)}.
- *
- * @return A string representation of this term.
- */
- public String serialize() {
- if (serialized != null) {
- return serialized;
- }
- String prefix;
- if (this instanceof Regex) {
- prefix = REGEX_PREFIX;
- } else if (this instanceof Fair) {
- prefix = FAIR_PREFIX;
- } else {
- prefix = UNKNOWN_PREFIX;
- }
- serialized = String.format("%s=%s", prefix, value);
- return serialized;
+ Term getCachedStableTerm() {
+ return stableTerm;
}
- /**
- * Parse a string representation of a {@link Term} produced by
- * {@link #serialize()}.
- *
- * @param string A string representation produced by {@link #serialize()}.
- * @return The parsed term, or empty if the method was not able to parse.
- */
- @JsonIgnore
- public static Optional deserialize(String string) {
- if (string == null) {
- return Optional.empty();
- }
-
- if (string.startsWith(REGEX_PREFIX)) {
- return Optional.of(regex(string.substring(REGEX_PREFIX.length() + 1)));
- } else if (string.startsWith(FAIR_PREFIX)) {
- return Optional.of(fair(string.substring(FAIR_PREFIX.length() + 1)));
- } else {
- return Optional.empty();
- }
+ void setCachedStableTerm(Term stableTerm) {
+ this.stableTerm = stableTerm;
}
@Override
public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
Term term = (Term) o;
- return Objects.equals(term.serialize(), serialize());
+ return Objects.equals(serialize(), term.serialize());
}
@Override
public int hashCode() {
- return Objects.hash(serialize());
+ return serialize().hashCode();
}
@Override
@@ -550,51 +186,59 @@ public String toString() {
return serialize();
}
- /**
- * This term represents a Fast Automaton Internal Representation (FAIR).
- *
- * You can learn more about FAIR in our
- * documentation.
- *
- */
- public static final class Fair extends Term {
- /**
- * Create a new instance.
- *
- * @param fair The FAIR.
- */
- public Fair(@JsonProperty("value") String fair) {
- super(fair);
+ public static final class RegexTerm extends Term {
+
+ RegexTerm(String value) {
+ super(value);
+ }
+
+ @Override
+ public Optional getPattern() {
+ return Optional.of(getValue());
+ }
+
+ @Override
+ public Optional getFair() {
+ return Optional.ofNullable(getCachedStableTerm()).map(t ->
+ t.getFair().orElse(null)
+ );
+ }
+
+ @Override
+ TermDto toDto() {
+ return new TermDto(new TermRegexDto().value(getValue()));
}
- @JsonProperty("value")
@Override
- public String getFair() {
- return getValue();
+ public String serialize() {
+ return "regex=" + getValue();
}
}
- /**
- * This term represents a regular expression.
- *
- * You can learn more about regular expression in our
- * documentation
- *
- */
- public static final class Regex extends Term {
- /**
- * Create a new instance.
- *
- * @param regex The regular expression pattern.
- */
- public Regex(@JsonProperty("value") String regex) {
- super(regex);
+ public static final class FairTerm extends Term {
+
+ FairTerm(String value) {
+ super(value);
+ }
+
+ @Override
+ public Optional getPattern() {
+ return Optional.ofNullable(this.pattern);
+ }
+
+ @Override
+ public Optional getFair() {
+ return Optional.of(getValue());
+ }
+
+ @Override
+ TermDto toDto() {
+ return new TermDto(new TermFairDto().value(getValue()));
}
- @JsonProperty("value")
@Override
- public String getPattern() {
- return getValue();
+ public String serialize() {
+ return "fair=" + getValue();
}
}
}
diff --git a/src/main/java/com/regexsolver/api/TermPropertiesMixin.java b/src/main/java/com/regexsolver/api/TermPropertiesMixin.java
new file mode 100644
index 0000000..a3db3cd
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/TermPropertiesMixin.java
@@ -0,0 +1,41 @@
+package com.regexsolver.api;
+
+import java.util.Optional;
+
+/**
+ * A mixin providing default property inference for Term analytics.
+ *
+ * Returns {@code Optional.empty()} when a property cannot be strictly inferred from the current data alone.
+ */
+abstract class TermPropertiesMixin {
+
+ /**
+ * Infers whether the term matches no strings at all.
+ *
+ * @return An {@code Optional} containing {@code true} if it definitely matches no strings,
+ * {@code false} if it matches at least one, or {@code Optional.empty()} if it cannot be inferred.
+ */
+ public Optional isEmpty() {
+ return Optional.empty();
+ }
+
+ /**
+ * Infers whether the term matches strictly the empty string ("").
+ *
+ * @return An {@code Optional} containing {@code true} if it definitely matches only the empty string,
+ * {@code false} if it matches other strings, or {@code Optional.empty()} if it cannot be inferred.
+ */
+ public Optional isEmptyString() {
+ return Optional.empty();
+ }
+
+ /**
+ * Infers whether the term matches all possible strings.
+ *
+ * @return An {@code Optional} containing {@code true} if it definitely matches all strings,
+ * {@code false} if it misses at least one string, or {@code Optional.empty()} if it cannot be inferred.
+ */
+ public Optional isTotal() {
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/dto/Cardinality.java b/src/main/java/com/regexsolver/api/dto/Cardinality.java
deleted file mode 100644
index d4689be..0000000
--- a/src/main/java/com/regexsolver/api/dto/Cardinality.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.regexsolver.api.dto;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-
-/**
- * Abstract class that represent the number of possible values.
- */
-@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
-@JsonSubTypes({
- @JsonSubTypes.Type(value = Cardinality.BigInteger.class, name = "bigInteger"),
- @JsonSubTypes.Type(value = Cardinality.Infinite.class, name = "infinite"),
- @JsonSubTypes.Type(value = Cardinality.Integer.class, name = "integer")
-})
-public abstract class Cardinality {
- /**
- * @return true if it has a finite number of values, false otherwise.
- */
- public abstract boolean isFinite();
-
- public abstract String toString();
-
- /**
- * An infinite number of possible values.
- */
- public static final class Infinite extends Cardinality {
- @Override
- public boolean isFinite() {
- return false;
- }
-
- @Override
- public String toString() {
- return "Infinite";
- }
- }
-
- /**
- * A finite number of possible values, but the number is too big to be computed.
- */
- public static final class BigInteger extends Cardinality {
- @Override
- public boolean isFinite() {
- return true;
- }
-
- @Override
- public String toString() {
- return "BigInteger";
- }
- }
-
- /**
- * A finite number of possible values, available in {@link #getCount()}.
- */
- public static final class Integer extends Cardinality {
- private final long count;
-
- /**
- * Create a new instance.
- *
- * @param count The number of possible values.
- */
- public Integer(@JsonProperty("value") long count) {
- this.count = count;
- }
-
- @Override
- public boolean isFinite() {
- return false;
- }
-
- /**
- * @return The number of possible values.
- */
- public long getCount() {
- return count;
- }
-
- @Override
- public String toString() {
- return String.format("Integer(%s)", count);
- }
- }
-}
diff --git a/src/main/java/com/regexsolver/api/dto/Length.java b/src/main/java/com/regexsolver/api/dto/Length.java
deleted file mode 100644
index 785870e..0000000
--- a/src/main/java/com/regexsolver/api/dto/Length.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.regexsolver.api.dto;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-
-import java.io.IOException;
-import java.util.Objects;
-import java.util.OptionalLong;
-
-/**
- * Contains the minimum and maximum length of possible values.
- */
-@JsonDeserialize(using = Length.LengthDeserializer.class)
-public final class Length {
- private final Long minimum;
- private final Long maximum;
-
- /**
- * @param minimum the minimum length of possible values, empty if is an empty
- * set.
- * @param maximum the maximum length of possible values, empty if the maximum
- * length is infinite or if is an empty set.
- */
- Length(Long minimum, Long maximum) {
- this.minimum = minimum;
- this.maximum = maximum;
- }
-
- /**
- * @return The minimum length of possible values, empty if is an empty set.
- */
- public OptionalLong getMinimum() {
- if (minimum == null) {
- return OptionalLong.empty();
- }
- return OptionalLong.of(minimum);
- }
-
- /**
- * @return The maximum length of possible values, empty if the maximum length is
- * infinite or if is an empty set.
- */
- public OptionalLong getMaximum() {
- if (maximum == null) {
- return OptionalLong.empty();
- }
- return OptionalLong.of(maximum);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this)
- return true;
- if (obj == null || obj.getClass() != this.getClass())
- return false;
- var that = (Length) obj;
- return Objects.equals(this.minimum, that.minimum) &&
- Objects.equals(this.maximum, that.maximum);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(minimum, maximum);
- }
-
- @Override
- public String toString() {
- return "Length[" +
- "minimum=" + minimum + ", " +
- "maximum=" + maximum + ']';
- }
-
- static class LengthDeserializer extends JsonDeserializer {
- @Override
- public Length deserialize(JsonParser jp, DeserializationContext ctx)
- throws IOException {
- JsonToken t = jp.currentToken();
- if (t == null)
- t = jp.nextToken();
-
- if (t == JsonToken.START_ARRAY) {
- Long[] arr = jp.readValueAs(Long[].class);
- if (arr == null || arr.length != 2) {
- throw new IOException("Expected [minimum,maximum] array.");
- }
- return new Length(arr[0], arr[1]);
- }
-
- if (t == JsonToken.START_OBJECT) {
- Long min = null;
- Long max = null;
-
- while (jp.nextToken() != JsonToken.END_OBJECT) {
- String field = jp.currentName();
- jp.nextToken(); // move to value
- if ("min".equals(field)) {
- min = jp.currentToken() == JsonToken.VALUE_NULL ? null : jp.getLongValue();
- } else if ("max".equals(field)) {
- max = jp.currentToken() == JsonToken.VALUE_NULL ? null : jp.getLongValue();
- } else {
- jp.skipChildren(); // ignore unknown fields
- }
- }
- return new Length(min, max);
- }
-
- if (t == JsonToken.VALUE_NULL) {
- return null;
- }
-
- throw new IOException("Expected [minimum,maximum] array, or {minimum,maximum} object.");
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/regexsolver/api/dto/package-info.java b/src/main/java/com/regexsolver/api/dto/package-info.java
deleted file mode 100644
index e518844..0000000
--- a/src/main/java/com/regexsolver/api/dto/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains simple objects.
- */
-package com.regexsolver.api.dto;
\ No newline at end of file
diff --git a/src/main/java/com/regexsolver/api/exception/ApiError.java b/src/main/java/com/regexsolver/api/exception/ApiError.java
deleted file mode 100644
index b18d74c..0000000
--- a/src/main/java/com/regexsolver/api/exception/ApiError.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.regexsolver.api.exception;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * Thrown when the API returns an error.
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class ApiError extends Exception {
- /**
- * Create a new instance.
- *
- * @param message The error message returned by the API.
- */
- public ApiError(@JsonProperty("message") String message) {
- super(String.format("The API returned the following error: %s", message));
- }
-}
diff --git a/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java b/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
deleted file mode 100644
index 4240691..0000000
--- a/src/main/java/com/regexsolver/api/exception/MissingAPITokenException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.regexsolver.api.exception;
-
-/**
- * Thrown if the API token has not been set as environment variable.
- */
-public class MissingAPITokenException extends RuntimeException {
- /**
- * The API token has not been set, set the environment variable REGEXSOLVER_API_TOKEN and call RegexSolverApiWrapper.initialize() to set it.
- * To generate a token go to RegexSolver Console.
- */
- public MissingAPITokenException() {
- super("The API token has not been set, set the environment variable REGEXSOLVER_API_TOKEN and call RegexSolverApiWrapper.initialize() to set it.\n" +
- "To generate a token go to https://console.regexsolver.com/.");
- }
-}
diff --git a/src/main/java/com/regexsolver/api/exception/package-info.java b/src/main/java/com/regexsolver/api/exception/package-info.java
deleted file mode 100644
index bc21fdf..0000000
--- a/src/main/java/com/regexsolver/api/exception/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains exceptions that can be thrown while using the library.
- */
-package com.regexsolver.api.exception;
\ No newline at end of file
diff --git a/src/main/java/com/regexsolver/api/exceptions/ApiException.java b/src/main/java/com/regexsolver/api/exceptions/ApiException.java
new file mode 100644
index 0000000..1bc2f8b
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/ApiException.java
@@ -0,0 +1,33 @@
+package com.regexsolver.api.exceptions;
+
+/** Base exception raised when the RegexSolver API returns an error response. */
+public class ApiException extends RegexSolverException {
+
+ private final int statusCode;
+ private final String errorCode;
+ private final String body;
+
+ public ApiException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message);
+ this.statusCode = statusCode;
+ this.errorCode = errorCode;
+ this.body = body;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ public String getBody() {
+ return body;
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/BadRequestException.java b/src/main/java/com/regexsolver/api/exceptions/BadRequestException.java
new file mode 100644
index 0000000..969c03c
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/BadRequestException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the API returns a 400 Bad Request error. */
+public class BadRequestException extends ApiException {
+
+ public BadRequestException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/ForbiddenException.java b/src/main/java/com/regexsolver/api/exceptions/ForbiddenException.java
new file mode 100644
index 0000000..f4bee72
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/ForbiddenException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the API returns a 403 Forbidden error. */
+public class ForbiddenException extends ApiException {
+
+ public ForbiddenException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/InternalServerException.java b/src/main/java/com/regexsolver/api/exceptions/InternalServerException.java
new file mode 100644
index 0000000..0f292f7
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/InternalServerException.java
@@ -0,0 +1,17 @@
+package com.regexsolver.api.exceptions;
+
+/**
+ * Raised when the API returns a 500 Internal Server Error.
+ * Indicates an unexpected failure or panic on the RegexSolver compute servers.
+ */
+public class InternalServerException extends ApiException {
+
+ public InternalServerException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/InvalidJsonException.java b/src/main/java/com/regexsolver/api/exceptions/InvalidJsonException.java
new file mode 100644
index 0000000..e86994e
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/InvalidJsonException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the provided JSON is invalid or cannot be parsed. */
+public class InvalidJsonException extends BadRequestException {
+
+ public InvalidJsonException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/InvalidNumberOfStringsToGenerateException.java b/src/main/java/com/regexsolver/api/exceptions/InvalidNumberOfStringsToGenerateException.java
new file mode 100644
index 0000000..e138a63
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/InvalidNumberOfStringsToGenerateException.java
@@ -0,0 +1,16 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the requested number of strings to generate is below the minimum or exceeds the maximum allowed. */
+public class InvalidNumberOfStringsToGenerateException
+ extends BadRequestException
+{
+
+ public InvalidNumberOfStringsToGenerateException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/InvalidTokenException.java b/src/main/java/com/regexsolver/api/exceptions/InvalidTokenException.java
new file mode 100644
index 0000000..eb5d31b
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/InvalidTokenException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the provided authentication token is invalid. */
+public class InvalidTokenException extends UnauthorizedException {
+
+ public InvalidTokenException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/MissingOrMalformedTokenException.java b/src/main/java/com/regexsolver/api/exceptions/MissingOrMalformedTokenException.java
new file mode 100644
index 0000000..cff1b35
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/MissingOrMalformedTokenException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the provided authentication token is missing or malformed. */
+public class MissingOrMalformedTokenException extends UnauthorizedException {
+
+ public MissingOrMalformedTokenException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/NotFoundException.java b/src/main/java/com/regexsolver/api/exceptions/NotFoundException.java
new file mode 100644
index 0000000..1fd8d4e
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/NotFoundException.java
@@ -0,0 +1,16 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the API returns a 404 Not Found error.
+ * Indicates that the requested API endpoint or resource does not exist.
+ */
+public class NotFoundException extends ApiException {
+
+ public NotFoundException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/QuotaExceededException.java b/src/main/java/com/regexsolver/api/exceptions/QuotaExceededException.java
new file mode 100644
index 0000000..cdb8157
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/QuotaExceededException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when your account's monthly compute quota has been exceeded. */
+public class QuotaExceededException extends ForbiddenException {
+
+ public QuotaExceededException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/RegexSolverException.java b/src/main/java/com/regexsolver/api/exceptions/RegexSolverException.java
new file mode 100644
index 0000000..531fa0c
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/RegexSolverException.java
@@ -0,0 +1,11 @@
+package com.regexsolver.api.exceptions;
+
+/**
+ * Base exception for all RegexSolver errors.
+ */
+public class RegexSolverException extends RuntimeException {
+
+ public RegexSolverException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/TimeoutExceededException.java b/src/main/java/com/regexsolver/api/exceptions/TimeoutExceededException.java
new file mode 100644
index 0000000..2fed7de
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/TimeoutExceededException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the execution of the request exceeds the provided `execution_timeout` or the maximum allowed for your current plan. */
+public class TimeoutExceededException extends BadRequestException {
+
+ public TimeoutExceededException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/TimeoutTooLargeException.java b/src/main/java/com/regexsolver/api/exceptions/TimeoutTooLargeException.java
new file mode 100644
index 0000000..dff3d3d
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/TimeoutTooLargeException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the requested `execution_timeout` exceeds the maximum allowed for your current plan. */
+public class TimeoutTooLargeException extends BadRequestException {
+
+ public TimeoutTooLargeException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/TooManyRequestsException.java b/src/main/java/com/regexsolver/api/exceptions/TooManyRequestsException.java
new file mode 100644
index 0000000..47b1904
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/TooManyRequestsException.java
@@ -0,0 +1,17 @@
+package com.regexsolver.api.exceptions;
+
+/**
+ * Raised when the API returns a 429 Too Many Requests error and max retries are exceeded.
+ * Indicates that your requests-per-second (req/s) rate limit has been exceeded.
+ */
+public class TooManyRequestsException extends ApiException {
+
+ public TooManyRequestsException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/TooManyTermsException.java b/src/main/java/com/regexsolver/api/exceptions/TooManyTermsException.java
new file mode 100644
index 0000000..21ac3f6
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/TooManyTermsException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the number of terms provided exceeds the maximum allowed. */
+public class TooManyTermsException extends BadRequestException {
+
+ public TooManyTermsException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/exceptions/UnauthorizedException.java b/src/main/java/com/regexsolver/api/exceptions/UnauthorizedException.java
new file mode 100644
index 0000000..73e5d9d
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/exceptions/UnauthorizedException.java
@@ -0,0 +1,14 @@
+package com.regexsolver.api.exceptions;
+
+/** Raised when the API returns a 401 Unauthorized error. */
+public class UnauthorizedException extends ApiException {
+
+ public UnauthorizedException(
+ String message,
+ int statusCode,
+ String errorCode,
+ String body
+ ) {
+ super(message, statusCode, errorCode, body);
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/generated/ApiClient.java b/src/main/java/com/regexsolver/api/generated/ApiClient.java
new file mode 100644
index 0000000..aa4356d
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/generated/ApiClient.java
@@ -0,0 +1,486 @@
+/*
+ * RegexSolver
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.1.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+package com.regexsolver.api.generated;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+import java.util.Optional;
+import java.util.zip.GZIPInputStream;
+import java.util.stream.Collectors;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Configuration and utility class for API clients.
+ *
+ * This class can be constructed and modified, then used to instantiate the
+ * various API classes. The API classes use the settings in this class to
+ * configure themselves, but otherwise do not store a link to this class.
+ *
+ * This class is mutable and not synchronized, so it is not thread-safe.
+ * The API classes generated from this are immutable and thread-safe.
+ *
+ * The setter methods of this class return the current object to facilitate
+ * a fluent style of configuration.
+ */
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+public class ApiClient {
+
+ protected HttpClient.Builder builder;
+ protected ObjectMapper mapper;
+ protected String scheme;
+ protected String host;
+ protected int port;
+ protected String basePath;
+ protected Consumer interceptor;
+ protected Consumer> responseInterceptor;
+ protected Consumer> asyncResponseInterceptor;
+ protected Duration readTimeout;
+ protected Duration connectTimeout;
+
+ public static String valueToString(Object value) {
+ if (value == null) {
+ return "";
+ }
+ if (value instanceof OffsetDateTime) {
+ return ((OffsetDateTime) value).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+ }
+ return value.toString();
+ }
+
+ /**
+ * URL encode a string in the UTF-8 encoding.
+ *
+ * @param s String to encode.
+ * @return URL-encoded representation of the input string.
+ */
+ public static String urlEncode(String s) {
+ return URLEncoder.encode(s, UTF_8).replaceAll("\\+", "%20");
+ }
+
+ /**
+ * Convert a URL query name/value parameter to a list of encoded {@link Pair}
+ * objects.
+ *
+ * The value can be null, in which case an empty list is returned.
+ *
+ * @param name The query name parameter.
+ * @param value The query value, which may not be a collection but may be
+ * null.
+ * @return A singleton list of the {@link Pair} objects representing the input
+ * parameters, which is encoded for use in a URL. If the value is null, an
+ * empty list is returned.
+ */
+ public static List parameterToPairs(String name, Object value) {
+ if (name == null || name.isEmpty() || value == null) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(new Pair(urlEncode(name), urlEncode(valueToString(value))));
+ }
+
+ /**
+ * Convert a URL query name/collection parameter to a list of encoded
+ * {@link Pair} objects.
+ *
+ * @param collectionFormat The swagger collectionFormat string (csv, tsv, etc).
+ * @param name The query name parameter.
+ * @param values A collection of values for the given query name, which may be
+ * null.
+ * @return A list of {@link Pair} objects representing the input parameters,
+ * which is encoded for use in a URL. If the values collection is null, an
+ * empty list is returned.
+ */
+ public static List parameterToPairs(
+ String collectionFormat, String name, Collection> values) {
+ if (name == null || name.isEmpty() || values == null || values.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ // get the collection format (default: csv)
+ String format = collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat;
+
+ // create the params based on the collection format
+ if ("multi".equals(format)) {
+ return values.stream()
+ .map(value -> new Pair(urlEncode(name), urlEncode(valueToString(value))))
+ .collect(Collectors.toList());
+ }
+
+ String delimiter;
+ switch(format) {
+ case "csv":
+ delimiter = urlEncode(",");
+ break;
+ case "ssv":
+ delimiter = urlEncode(" ");
+ break;
+ case "tsv":
+ delimiter = urlEncode("\t");
+ break;
+ case "pipes":
+ delimiter = urlEncode("|");
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal collection format: " + collectionFormat);
+ }
+
+ StringJoiner joiner = new StringJoiner(delimiter);
+ for (Object value : values) {
+ joiner.add(urlEncode(valueToString(value)));
+ }
+
+ return Collections.singletonList(new Pair(urlEncode(name), joiner.toString()));
+ }
+
+ /**
+ * Create an instance of ApiClient.
+ */
+ public ApiClient() {
+ this.builder = createDefaultHttpClientBuilder();
+ this.mapper = createDefaultObjectMapper();
+ updateBaseUri("https://api.regexsolver.com/v1");
+ interceptor = null;
+ readTimeout = null;
+ connectTimeout = null;
+ responseInterceptor = null;
+ asyncResponseInterceptor = null;
+ }
+
+ /**
+ * Create an instance of ApiClient.
+ *
+ * @param builder Http client builder.
+ * @param mapper Object mapper.
+ * @param baseUri Base URI
+ */
+ public ApiClient(HttpClient.Builder builder, ObjectMapper mapper, String baseUri) {
+ this.builder = builder;
+ this.mapper = mapper;
+ updateBaseUri(baseUri != null ? baseUri : "https://api.regexsolver.com/v1");
+ interceptor = null;
+ readTimeout = null;
+ connectTimeout = null;
+ responseInterceptor = null;
+ asyncResponseInterceptor = null;
+ }
+
+ public static ObjectMapper createDefaultObjectMapper() {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
+ mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+ mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+ mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
+ mapper.registerModule(new JavaTimeModule());
+ mapper.registerModule(new RFC3339JavaTimeModule());
+ return mapper;
+ }
+
+ protected final String getDefaultBaseUri() {
+ return basePath;
+ }
+
+ public static HttpClient.Builder createDefaultHttpClientBuilder() {
+ return HttpClient.newBuilder();
+ }
+
+ public final void updateBaseUri(String baseUri) {
+ URI uri = URI.create(baseUri);
+ scheme = uri.getScheme();
+ host = uri.getHost();
+ port = uri.getPort();
+ basePath = uri.getRawPath();
+ }
+
+ /**
+ * Set a custom {@link HttpClient.Builder} object to use when creating the
+ * {@link HttpClient} that is used by the API client.
+ *
+ * @param builder Custom client builder.
+ * @return This object.
+ */
+ public ApiClient setHttpClientBuilder(HttpClient.Builder builder) {
+ this.builder = builder;
+ return this;
+ }
+
+ /**
+ * Get an {@link HttpClient} based on the current {@link HttpClient.Builder}.
+ *
+ * The returned object is immutable and thread-safe.
+ *
+ * @return The HTTP client.
+ */
+ public HttpClient getHttpClient() {
+ return builder.build();
+ }
+
+ /**
+ * Set a custom {@link ObjectMapper} to serialize and deserialize the request
+ * and response bodies.
+ *
+ * @param mapper Custom object mapper.
+ * @return This object.
+ */
+ public ApiClient setObjectMapper(ObjectMapper mapper) {
+ this.mapper = mapper;
+ return this;
+ }
+
+ /**
+ * Get a copy of the current {@link ObjectMapper}.
+ *
+ * @return A copy of the current object mapper.
+ */
+ public ObjectMapper getObjectMapper() {
+ return mapper.copy();
+ }
+
+ /**
+ * Set a custom host name for the target service.
+ *
+ * @param host The host name of the target service.
+ * @return This object.
+ */
+ public ApiClient setHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ /**
+ * Set a custom port number for the target service.
+ *
+ * @param port The port of the target service. Set this to -1 to reset the
+ * value to the default for the scheme.
+ * @return This object.
+ */
+ public ApiClient setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * Set a custom base path for the target service, for example '/v2'.
+ *
+ * @param basePath The base path against which the rest of the path is
+ * resolved.
+ * @return This object.
+ */
+ public ApiClient setBasePath(String basePath) {
+ this.basePath = basePath;
+ return this;
+ }
+
+ /**
+ * Get the base URI to resolve the endpoint paths against.
+ *
+ * @return The complete base URI that the rest of the API parameters are
+ * resolved against.
+ */
+ public String getBaseUri() {
+ return scheme + "://" + host + (port == -1 ? "" : ":" + port) + basePath;
+ }
+
+ /**
+ * Set a custom scheme for the target service, for example 'https'.
+ *
+ * @param scheme The scheme of the target service
+ * @return This object.
+ */
+ public ApiClient setScheme(String scheme){
+ this.scheme = scheme;
+ return this;
+ }
+
+ /**
+ * Set a custom request interceptor.
+ *
+ * A request interceptor is a mechanism for altering each request before it
+ * is sent. After the request has been fully configured but not yet built, the
+ * request builder is passed into this function for further modification,
+ * after which it is sent out.
+ *
+ * This is useful for altering the requests in a custom manner, such as
+ * adding headers. It could also be used for logging and monitoring.
+ *
+ * @param interceptor A function invoked before creating each request. A value
+ * of null resets the interceptor to a no-op.
+ * @return This object.
+ */
+ public ApiClient setRequestInterceptor(Consumer interceptor) {
+ this.interceptor = interceptor;
+ return this;
+ }
+
+ /**
+ * Get the custom interceptor.
+ *
+ * @return The custom interceptor that was set, or null if there isn't any.
+ */
+ public Consumer getRequestInterceptor() {
+ return interceptor;
+ }
+
+ /**
+ * Set a custom response interceptor.
+ *
+ * This is useful for logging, monitoring or extraction of header variables
+ *
+ * @param interceptor A function invoked before creating each request. A value
+ * of null resets the interceptor to a no-op.
+ * @return This object.
+ */
+ public ApiClient setResponseInterceptor(Consumer> interceptor) {
+ this.responseInterceptor = interceptor;
+ return this;
+ }
+
+ /**
+ * Get the custom response interceptor.
+ *
+ * @return The custom interceptor that was set, or null if there isn't any.
+ */
+ public Consumer> getResponseInterceptor() {
+ return responseInterceptor;
+ }
+
+ /**
+ * Set a custom async response interceptor. Use this interceptor when asyncNative is set to 'true'.
+ *
+ * This is useful for logging, monitoring or extraction of header variables
+ *
+ * @param interceptor A function invoked before creating each request. A value
+ * of null resets the interceptor to a no-op.
+ * @return This object.
+ */
+ public ApiClient setAsyncResponseInterceptor(Consumer> interceptor) {
+ this.asyncResponseInterceptor = interceptor;
+ return this;
+ }
+
+ /**
+ * Get the custom async response interceptor. Use this interceptor when asyncNative is set to 'true'.
+ *
+ * @return The custom interceptor that was set, or null if there isn't any.
+ */
+ public Consumer> getAsyncResponseInterceptor() {
+ return asyncResponseInterceptor;
+ }
+
+ /**
+ * Set the read timeout for the http client.
+ *
+ * This is the value used by default for each request, though it can be
+ * overridden on a per-request basis with a request interceptor.
+ *
+ * @param readTimeout The read timeout used by default by the http client.
+ * Setting this value to null resets the timeout to an
+ * effectively infinite value.
+ * @return This object.
+ */
+ public ApiClient setReadTimeout(Duration readTimeout) {
+ this.readTimeout = readTimeout;
+ return this;
+ }
+
+ /**
+ * Get the read timeout that was set.
+ *
+ * @return The read timeout, or null if no timeout was set. Null represents
+ * an infinite wait time.
+ */
+ public Duration getReadTimeout() {
+ return readTimeout;
+ }
+ /**
+ * Sets the connect timeout (in milliseconds) for the http client.
+ *
+ * In the case where a new connection needs to be established, if
+ * the connection cannot be established within the given {@code
+ * duration}, then {@link HttpClient#send(HttpRequest,BodyHandler)
+ * HttpClient::send} throws an {@link HttpConnectTimeoutException}, or
+ * {@link HttpClient#sendAsync(HttpRequest,BodyHandler)
+ * HttpClient::sendAsync} completes exceptionally with an
+ * {@code HttpConnectTimeoutException}. If a new connection does not
+ * need to be established, for example if a connection can be reused
+ * from a previous request, then this timeout duration has no effect.
+ *
+ * @param connectTimeout connection timeout in milliseconds
+ *
+ * @return This object.
+ */
+ public ApiClient setConnectTimeout(Duration connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ this.builder.connectTimeout(connectTimeout);
+ return this;
+ }
+
+ /**
+ * Get connection timeout (in milliseconds).
+ *
+ * @return Timeout in milliseconds
+ */
+ public Duration getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ /**
+ * Returns the response body InputStream, transparently decoding gzip-compressed
+ * payloads when the server sets {@code Content-Encoding: gzip}.
+ *
+ * @param response HTTP response whose body should be consumed
+ * @return Original or decompressed InputStream for the response body
+ * @throws IOException if the response body cannot be accessed or wrapping fails
+ */
+ public static InputStream getResponseBody(HttpResponse response) throws IOException {
+ if (response == null) {
+ return null;
+ }
+ InputStream body = response.body();
+ if (body == null) {
+ return null;
+ }
+ Optional encoding = response.headers().firstValue("Content-Encoding");
+ if (encoding.isPresent()) {
+ for (String token : encoding.get().split(",")) {
+ if ("gzip".equalsIgnoreCase(token.trim())) {
+ return new GZIPInputStream(body, 8192);
+ }
+ }
+ }
+ return body;
+ }
+
+}
diff --git a/src/main/java/com/regexsolver/api/generated/ApiException.java b/src/main/java/com/regexsolver/api/generated/ApiException.java
new file mode 100644
index 0000000..e742752
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/generated/ApiException.java
@@ -0,0 +1,92 @@
+/*
+ * RegexSolver
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.1.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+package com.regexsolver.api.generated;
+
+import java.net.http.HttpHeaders;
+
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+public class ApiException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private int code = 0;
+ private HttpHeaders responseHeaders = null;
+ private String responseBody = null;
+
+ public ApiException() {}
+
+ public ApiException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public ApiException(String message) {
+ super(message);
+ }
+
+ public ApiException(String message, Throwable throwable, int code, HttpHeaders responseHeaders, String responseBody) {
+ super(message, throwable);
+ this.code = code;
+ this.responseHeaders = responseHeaders;
+ this.responseBody = responseBody;
+ }
+
+ public ApiException(String message, int code, HttpHeaders responseHeaders, String responseBody) {
+ this(message, (Throwable) null, code, responseHeaders, responseBody);
+ }
+
+ public ApiException(String message, Throwable throwable, int code, HttpHeaders responseHeaders) {
+ this(message, throwable, code, responseHeaders, null);
+ }
+
+ public ApiException(int code, HttpHeaders responseHeaders, String responseBody) {
+ this((String) null, (Throwable) null, code, responseHeaders, responseBody);
+ }
+
+ public ApiException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public ApiException(int code, String message, HttpHeaders responseHeaders, String responseBody) {
+ this(code, message);
+ this.responseHeaders = responseHeaders;
+ this.responseBody = responseBody;
+ }
+
+ /**
+ * Get the HTTP status code.
+ *
+ * @return HTTP status code
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * Get the HTTP response headers.
+ *
+ * @return Headers as an HttpHeaders object
+ */
+ public HttpHeaders getResponseHeaders() {
+ return responseHeaders;
+ }
+
+ /**
+ * Get the HTTP response body.
+ *
+ * @return Response body in the form of string
+ */
+ public String getResponseBody() {
+ return responseBody;
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/generated/ApiResponse.java b/src/main/java/com/regexsolver/api/generated/ApiResponse.java
new file mode 100644
index 0000000..2dc7fe1
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/generated/ApiResponse.java
@@ -0,0 +1,60 @@
+/*
+ * RegexSolver
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.1.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+package com.regexsolver.api.generated;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * API response returned by API call.
+ *
+ * @param The type of data that is deserialized from response body
+ */
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+public class ApiResponse {
+ final private int statusCode;
+ final private Map> headers;
+ final private T data;
+
+ /**
+ * @param statusCode The status code of HTTP response
+ * @param headers The headers of HTTP response
+ */
+ public ApiResponse(int statusCode, Map> headers) {
+ this(statusCode, headers, null);
+ }
+
+ /**
+ * @param statusCode The status code of HTTP response
+ * @param headers The headers of HTTP response
+ * @param data The object deserialized from response bod
+ */
+ public ApiResponse(int statusCode, Map> headers, T data) {
+ this.statusCode = statusCode;
+ this.headers = headers;
+ this.data = data;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public Map> getHeaders() {
+ return headers;
+ }
+
+ public T getData() {
+ return data;
+ }
+}
diff --git a/src/main/java/com/regexsolver/api/generated/Configuration.java b/src/main/java/com/regexsolver/api/generated/Configuration.java
new file mode 100644
index 0000000..0d258b5
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/generated/Configuration.java
@@ -0,0 +1,63 @@
+/*
+ * RegexSolver
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.1.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+package com.regexsolver.api.generated;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+public class Configuration {
+ public static final String VERSION = "1.1.0";
+
+ private static final AtomicReference defaultApiClient = new AtomicReference<>();
+ private static volatile Supplier apiClientFactory = ApiClient::new;
+
+ /**
+ * Get the default API client, which would be used when creating API instances without providing an API client.
+ *
+ * @return Default API client
+ */
+ public static ApiClient getDefaultApiClient() {
+ ApiClient client = defaultApiClient.get();
+ if (client == null) {
+ client = defaultApiClient.updateAndGet(val -> {
+ if (val != null) { // changed by another thread
+ return val;
+ }
+ return apiClientFactory.get();
+ });
+ }
+ return client;
+ }
+
+ /**
+ * Set the default API client, which would be used when creating API instances without providing an API client.
+ *
+ * @param apiClient API client
+ */
+ public static void setDefaultApiClient(ApiClient apiClient) {
+ defaultApiClient.set(apiClient);
+ }
+
+ /**
+ * set the callback used to create new ApiClient objects
+ */
+ public static void setApiClientFactory(Supplier factory) {
+ apiClientFactory = Objects.requireNonNull(factory);
+ }
+
+ private Configuration() {
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/regexsolver/api/generated/JSON.java b/src/main/java/com/regexsolver/api/generated/JSON.java
new file mode 100644
index 0000000..e265398
--- /dev/null
+++ b/src/main/java/com/regexsolver/api/generated/JSON.java
@@ -0,0 +1,261 @@
+/*
+ * RegexSolver
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.1.0
+ *
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+package com.regexsolver.api.generated;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.regexsolver.api.generated.model.*;
+
+import java.text.DateFormat;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+public class JSON {
+ private ObjectMapper mapper;
+
+ public JSON() {
+ mapper = JsonMapper.builder()
+ .serializationInclusion(JsonInclude.Include.NON_NULL)
+ .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .enable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
+ .enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
+ .defaultDateFormat(new RFC3339DateFormat())
+ .addModule(new JavaTimeModule())
+ .build();
+ }
+
+ /**
+ * Set the date format for JSON (de)serialization with Date properties.
+ *
+ * @param dateFormat Date format
+ */
+ public void setDateFormat(DateFormat dateFormat) {
+ mapper.setDateFormat(dateFormat);
+ }
+
+ /**
+ * Get the object mapper
+ *
+ * @return object mapper
+ */
+ public ObjectMapper getMapper() { return mapper; }
+
+ /**
+ * Returns the target model class that should be used to deserialize the input data.
+ * The discriminator mappings are used to determine the target model class.
+ *
+ * @param node The input data.
+ * @param modelClass The class that contains the discriminator mappings.
+ *
+ * @return the target model class.
+ */
+ public static Class> getClassForElement(JsonNode node, Class> modelClass) {
+ ClassDiscriminatorMapping cdm = modelDiscriminators.get(modelClass);
+ if (cdm != null) {
+ return cdm.getClassForElement(node, new HashSet>());
+ }
+ return null;
+ }
+
+ /**
+ * Helper class to register the discriminator mappings.
+ */
+ @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-28T15:29:36.647057244+01:00[Europe/Zurich]", comments = "Generator version: 7.21.0")
+ private static class ClassDiscriminatorMapping {
+ // The model class name.
+ Class> modelClass;
+ // The name of the discriminator property.
+ String discriminatorName;
+ // The discriminator mappings for a model class.
+ Map> discriminatorMappings;
+
+ // Constructs a new class discriminator.
+ ClassDiscriminatorMapping(Class> cls, String propertyName, Map> mappings) {
+ modelClass = cls;
+ discriminatorName = propertyName;
+ discriminatorMappings = new HashMap>();
+ if (mappings != null) {
+ discriminatorMappings.putAll(mappings);
+ }
+ }
+
+ // Return the name of the discriminator property for this model class.
+ String getDiscriminatorPropertyName() {
+ return discriminatorName;
+ }
+
+ // Return the discriminator value or null if the discriminator is not
+ // present in the payload.
+ String getDiscriminatorValue(JsonNode node) {
+ // Determine the value of the discriminator property in the input data.
+ if (discriminatorName != null) {
+ // Get the value of the discriminator property, if present in the input payload.
+ node = node.get(discriminatorName);
+ if (node != null && node.isValueNode()) {
+ String discrValue = node.asText();
+ if (discrValue != null) {
+ return discrValue;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the target model class that should be used to deserialize the input data.
+ * This function can be invoked for anyOf/oneOf composed models with discriminator mappings.
+ * The discriminator mappings are used to determine the target model class.
+ *
+ * @param node The input data.
+ * @param visitedClasses The set of classes that have already been visited.
+ *
+ * @return the target model class.
+ */
+ Class> getClassForElement(JsonNode node, Set> visitedClasses) {
+ if (visitedClasses.contains(modelClass)) {
+ // Class has already been visited.
+ return null;
+ }
+ // Determine the value of the discriminator property in the input data.
+ String discrValue = getDiscriminatorValue(node);
+ if (discrValue == null) {
+ return null;
+ }
+ Class> cls = discriminatorMappings.get(discrValue);
+ // It may not be sufficient to return this cls directly because that target class
+ // may itself be a composed schema, possibly with its own discriminator.
+ visitedClasses.add(modelClass);
+ for (Class> childClass : discriminatorMappings.values()) {
+ ClassDiscriminatorMapping childCdm = modelDiscriminators.get(childClass);
+ if (childCdm == null) {
+ continue;
+ }
+ if (!discriminatorName.equals(childCdm.discriminatorName)) {
+ discrValue = getDiscriminatorValue(node);
+ if (discrValue == null) {
+ continue;
+ }
+ }
+ if (childCdm != null) {
+ // Recursively traverse the discriminator mappings.
+ Class> childDiscr = childCdm.getClassForElement(node, visitedClasses);
+ if (childDiscr != null) {
+ return childDiscr;
+ }
+ }
+ }
+ return cls;
+ }
+ }
+
+ /**
+ * Returns true if inst is an instance of modelClass in the OpenAPI model hierarchy.
+ *
+ * The Java class hierarchy is not implemented the same way as the OpenAPI model hierarchy,
+ * so it's not possible to use the instanceof keyword.
+ *
+ * @param modelClass A OpenAPI model class.
+ * @param inst The instance object.
+ * @param visitedClasses The set of classes that have already been visited.
+ *
+ * @return true if inst is an instance of modelClass in the OpenAPI model hierarchy.
+ */
+ public static boolean isInstanceOf(Class> modelClass, Object inst, Set> visitedClasses) {
+ if (modelClass.isInstance(inst)) {
+ // This handles the 'allOf' use case with single parent inheritance.
+ return true;
+ }
+ if (visitedClasses.contains(modelClass)) {
+ // This is to prevent infinite recursion when the composed schemas have
+ // a circular dependency.
+ return false;
+ }
+ visitedClasses.add(modelClass);
+
+ // Traverse the oneOf/anyOf composed schemas.
+ Map