From bbe78eaa3c08877cf8c0e6c101254e553a9e2a55 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 5 Nov 2024 20:08:40 +0000 Subject: [PATCH 01/57] feat: create sample code Signed-off-by: Otavio Santana --- .../main/java/jakarta/data/FilterType.java | 76 +++++++++++++++++++ .../main/java/jakarta/data/repository/Is.java | 57 ++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 api/src/main/java/jakarta/data/FilterType.java create mode 100644 api/src/main/java/jakarta/data/repository/Is.java diff --git a/api/src/main/java/jakarta/data/FilterType.java b/api/src/main/java/jakarta/data/FilterType.java new file mode 100644 index 000000000..805aa3fe8 --- /dev/null +++ b/api/src/main/java/jakarta/data/FilterType.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +/** + * Enum representing common filter types used to compare, match, or restrict values in Jakarta Data queries. + * The {@code FilterType} enum defines a set of operators to specify how values should be compared or matched + * in data filtering, providing flexibility for various querying needs across different contexts. + */ +public enum FilterType { + + /** + * Matches records where the field value is exactly equal to the specified value. + * Often used for precise matching on identifiers, names, or other exact-value fields. + */ + Equal, + + /** + * Matches records where the field value is greater than the specified value. + * Commonly used for numerical fields (e.g., dates, prices) to retrieve values above a threshold. + */ + GreaterThan, + + /** + * Matches records where the field value is greater than or equal to the specified value. + * Useful for inclusive range queries to include a specified boundary. + */ + GreaterThanEqual, + + /** + * Filters records where the field value is contained within a specified collection or set of values. + * Often applied to match multiple values (e.g., category lists, status collections). + */ + In, + + /** + * Matches records where the field value is less than the specified value. + * Frequently used in numerical comparisons to retrieve values below a threshold. + */ + LessThan, + + /** + * Matches records where the field value is less than or equal to the specified value. + * Suitable for inclusive range queries that include the specified boundary. + */ + LessThanEqual, + + /** + * Matches records where the field value conforms to a specified pattern, often utilizing wildcards. + * Commonly applied to string fields, allowing partial matches (e.g., names containing a substring). + */ + Like, + + /** + * Matches records where the field value does not equal the specified value. + * Typically used to exclude specific values from results, providing inverse filtering. + */ + Not; +} + + diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java new file mode 100644 index 000000000..251904f6e --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.repository; + +import jakarta.data.FilterType; + +/** + * Annotation to specify query conditions on method parameters in Jakarta Data repository interfaces. + * The {@code @Is} annotation, combined with the {@link FilterType} enum, enables flexible and expressive + * filtering in repository queries. + * Usage Example: + *
{@code
+ * @Find
+ * Page search(@Is(Conditions.Equal) String name); // Finds products where the name matches exactly.
+ * }
+ * + * Example for Negation: + * + *
{@code
+ * @Find
+ * Page search(@Is(value = Conditions.Equal, not = true) String excludedName); // Excludes products with the specified name.
+ * }
+ * + */ +public @interface Is { + + /** + * Defines the condition to apply to the annotated parameter. + * + * @return the specified {@link FilterType} value. + */ + FilterType value(); + + /** + * When set to true, applies the negation of the specified condition. + * For example, using `not = true` with {@link FilterType#Equal} will search for records + * where the field is not equal to the specified value. + * + * @return whether to negate the specified condition. + */ + boolean not() default false; +} From e48ca601ce375f89df2fbdd9de3152944ba7f732 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 5 Nov 2024 20:29:46 +0000 Subject: [PATCH 02/57] feat: create filter and criteria Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Criteria.java | 67 ++++++++++ .../main/java/jakarta/data/FilterType.java | 22 ++-- .../data/metamodel/FilterableAttribute.java | 118 ++++++++++++++++++ .../data/repository/CriteriaRepository.java | 66 ++++++++++ 4 files changed, 262 insertions(+), 11 deletions(-) create mode 100644 api/src/main/java/jakarta/data/Criteria.java create mode 100644 api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java create mode 100644 api/src/main/java/jakarta/data/repository/CriteriaRepository.java diff --git a/api/src/main/java/jakarta/data/Criteria.java b/api/src/main/java/jakarta/data/Criteria.java new file mode 100644 index 000000000..109619842 --- /dev/null +++ b/api/src/main/java/jakarta/data/Criteria.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +import java.util.List; + +/** + * Represents a criteria condition for repository queries, defining a field, an operator, + * a comparison value, and an optional negation flag for flexible query construction. + */ +public record Criteria(String field, FilterType operator, Object value, boolean negate) { + + /** + * Constructs a criteria condition without negation. + * + * @param field the name of the field to apply the criteria to (e.g., "title" or "publicationDate"). + * @param operator the operator defining the comparison or condition (e.g., `FilterType.Like`). + * @param value the value to compare the field against. + */ + public Criteria(String field, FilterType operator, Object value) { + this(field, operator, value, false); + } + + + /** + * Creates a basic criteria condition without negation. + */ + public static Criteria where(String field, FilterType operator, Object value) { + return new Criteria(field, operator, value, false); + } + + /** + * Creates a criteria condition with negation. + */ + public static Criteria not(String field, FilterType operator, Object value) { + return new Criteria(field, operator, value, true); + } + + /** + * Creates a LIKE criteria with a pattern, without negation. + */ + public static Criteria like(String field, String pattern) { + return new Criteria(field, FilterType.LIKE, pattern, false); + } + + /** + * Creates a BETWEEN criteria for range queries, without negation. + */ + public static Criteria between(String field, Object start, Object end) { + return new Criteria(field, FilterType.BETWEEN, List.of(start, end), false); + } +} diff --git a/api/src/main/java/jakarta/data/FilterType.java b/api/src/main/java/jakarta/data/FilterType.java index 805aa3fe8..520dc30da 100644 --- a/api/src/main/java/jakarta/data/FilterType.java +++ b/api/src/main/java/jakarta/data/FilterType.java @@ -17,60 +17,60 @@ */ package jakarta.data; + /** * Enum representing common filter types used to compare, match, or restrict values in Jakarta Data queries. * The {@code FilterType} enum defines a set of operators to specify how values should be compared or matched * in data filtering, providing flexibility for various querying needs across different contexts. */ public enum FilterType { - /** * Matches records where the field value is exactly equal to the specified value. * Often used for precise matching on identifiers, names, or other exact-value fields. */ - Equal, + EQUAL, /** * Matches records where the field value is greater than the specified value. * Commonly used for numerical fields (e.g., dates, prices) to retrieve values above a threshold. */ - GreaterThan, + GREATER_THAN, /** * Matches records where the field value is greater than or equal to the specified value. * Useful for inclusive range queries to include a specified boundary. */ - GreaterThanEqual, + GREATER_THAN_EQUAL, /** * Filters records where the field value is contained within a specified collection or set of values. * Often applied to match multiple values (e.g., category lists, status collections). */ - In, + IN, /** * Matches records where the field value is less than the specified value. * Frequently used in numerical comparisons to retrieve values below a threshold. */ - LessThan, + LESS_THAN, /** * Matches records where the field value is less than or equal to the specified value. * Suitable for inclusive range queries that include the specified boundary. */ - LessThanEqual, + LESS_THAN_EQUAL, /** * Matches records where the field value conforms to a specified pattern, often utilizing wildcards. * Commonly applied to string fields, allowing partial matches (e.g., names containing a substring). */ - Like, + LIKE, /** - * Matches records where the field value does not equal the specified value. - * Typically used to exclude specific values from results, providing inverse filtering. + * Matches records where the field value falls within a specified range of values. + * Typically used for range comparisons, such as finding dates within a start and end period. */ - Not; + BETWEEN; } diff --git a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java new file mode 100644 index 000000000..2f7659380 --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import jakarta.data.Criteria; + +import java.util.Collection; + +/** + * Represents an entity attribute in the {@link StaticMetamodel} that supports filtering conditions. + * This interface provides methods for defining common query conditions, allowing the attribute to be used + * in filtering expressions such as `_Book.title.like("Jakarta Data%")`. + * + *

Example usage:

+ *
{@code
+ * // Define criteria for filtering
+ * Criteria titleCriteria = _Book.title.like("Jakarta Data%");
+ * Criteria dateCriteria = _Book.publicationDate.between(pastDate, LocalDate.now());
+ * Criteria authorCriteria = _Book.author.equal("John Doe");
+ *
+ * // Apply criteria in a repository query
+ * repo.books(List.of(titleCriteria, dateCriteria, authorCriteria));
+ * }
+ * + * @param the type of the entity class in the static metamodel. + * @param the type of the attribute's value. + */ +public interface FilterableAttribute extends Attribute { + + /** + * Creates an equality condition for the attribute, matching the specified value. + * + * @param value the value to compare with the attribute. + * @return a criteria condition representing "attribute = value". + */ + Criteria equal(V value); + + /** + * Creates an inequality condition for the attribute, excluding the specified value. + * + * @param value the value to exclude. + * @return a criteria condition representing "attribute != value". + */ + Criteria notEqual(V value); + + /** + * Creates a "like" condition for textual attributes, matching the specified pattern. + * This is typically used for partial matches in string fields. + * + * @param pattern the pattern to match, often using wildcards. + * @return a criteria condition representing "attribute LIKE pattern". + */ + Criteria like(String pattern); + + /** + * Creates a "greater than" condition for the attribute, matching values greater than the specified value. + * + * @param value the minimum value to match. + * @return a criteria condition representing "attribute > value". + */ + Criteria greaterThan(V value); + + /** + * Creates a "greater than or equal to" condition for the attribute, matching values greater than or equal to the specified value. + * + * @param value the minimum value to match inclusively. + * @return a criteria condition representing "attribute >= value". + */ + Criteria greaterThanOrEqual(V value); + + /** + * Creates a "less than" condition for the attribute, matching values less than the specified value. + * + * @param value the maximum value to match. + * @return a criteria condition representing "attribute < value". + */ + Criteria lessThan(V value); + + /** + * Creates a "less than or equal to" condition for the attribute, matching values less than or equal to the specified value. + * + * @param value the maximum value to match inclusively. + * @return a criteria condition representing "attribute <= value". + */ + Criteria lessThanOrEqual(V value); + + /** + * Creates a "between" condition for the attribute, matching values within the specified range. + * + * @param start the lower bound of the range. + * @param end the upper bound of the range. + * @return a criteria condition representing "attribute BETWEEN start AND end". + */ + Criteria between(V start, V end); + + /** + * Creates an "in" condition for the attribute, matching any of the specified values. + * + * @param values the collection of values to match. + * @return a criteria condition representing "attribute IN (values)". + */ + Criteria in(Collection values); +} diff --git a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java new file mode 100644 index 000000000..fb025a3e4 --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java @@ -0,0 +1,66 @@ +package jakarta.data.repository; + +import jakarta.data.Criteria; +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; + +import java.util.List; + +/** + * Repository interface that extends {@link DataRepository} to add support for dynamic filtering + * using {@link Criteria} objects with optional pagination. + * + *

This repository allows flexible querying by accepting varargs of criteria conditions + * for filtering and supports pagination to retrieve results in pages or as a full list.

+ * + *

Example usage:

+ * + *
+ * @Inject
+ * DriverLicenses licenses;
+ *
+ * // Define and apply criteria using metamodel attributes
+ * PageRequest pageRequest = PageRequest.of(0, 10);
+ * Page pagedResults = licenses.findByCriteria(pageRequest,
+ *     _DriverLicense.license.like("ABC%"),
+ *     _DriverLicense.expiry.greaterThan(LocalDate.now())
+ * );
+ *
+ * // Fetch results without pagination
+ * List allResults = licenses.findByCriteria(
+ *     _DriverLicense.license.like("ABC%"),
+ *     _DriverLicense.expiry.greaterThan(LocalDate.now())
+ * );
+ * 
+ * + * @param the type of the primary entity class of the repository. + * @param the type of the unique identifier field or property of the primary entity. + */ +public interface CriteriaRepository extends DataRepository { + + + /** + * Finds entities by applying the specified criteria conditions, with support for pagination. + * + * @param pageRequest the pagination information including page number and size. + * @param criteria the varargs of criteria conditions to filter the results. + * @return a page of entities that match the criteria. + */ + Page filter(PageRequest pageRequest, Criteria... criteria); + + /** + * Finds all entities by applying the specified criteria conditions, returning results as a list without pagination. + * + * @param criteria the varargs of criteria conditions to filter the results. + * @return a list of entities that match the criteria. + */ + List filter(Criteria... criteria); + + /** + * Counts entities by applying the specified criteria conditions. + * + * @param criteria the varargs of criteria conditions to filter the results. + * @return the count of entities that match the criteria. + */ + long countBy(Criteria... criteria); +} From a5bdaa3ef17e8b3dcc9f067f9c78ba9daa341564 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 5 Nov 2024 20:36:27 +0000 Subject: [PATCH 03/57] feat: include header to CriteriaRepository Signed-off-by: Otavio Santana --- .../data/repository/CriteriaRepository.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java index fb025a3e4..279bbeb5f 100644 --- a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java +++ b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package jakarta.data.repository; import jakarta.data.Criteria; From 113d0c2efdfe97bdaf6df8e49eb6d99fa1e7d854 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 03:04:46 +0000 Subject: [PATCH 04/57] feat: rename core classes to Restriction and Operator Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Criteria.java | 67 ------------- .../data/{FilterType.java => Operator.java} | 33 ++++--- .../main/java/jakarta/data/Restriction.java | 96 +++++++++++++++++++ .../data/metamodel/FilterableAttribute.java | 20 ++-- .../data/repository/CriteriaRepository.java | 10 +- .../main/java/jakarta/data/repository/Is.java | 10 +- 6 files changed, 134 insertions(+), 102 deletions(-) delete mode 100644 api/src/main/java/jakarta/data/Criteria.java rename api/src/main/java/jakarta/data/{FilterType.java => Operator.java} (58%) create mode 100644 api/src/main/java/jakarta/data/Restriction.java diff --git a/api/src/main/java/jakarta/data/Criteria.java b/api/src/main/java/jakarta/data/Criteria.java deleted file mode 100644 index 109619842..000000000 --- a/api/src/main/java/jakarta/data/Criteria.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data; - -import java.util.List; - -/** - * Represents a criteria condition for repository queries, defining a field, an operator, - * a comparison value, and an optional negation flag for flexible query construction. - */ -public record Criteria(String field, FilterType operator, Object value, boolean negate) { - - /** - * Constructs a criteria condition without negation. - * - * @param field the name of the field to apply the criteria to (e.g., "title" or "publicationDate"). - * @param operator the operator defining the comparison or condition (e.g., `FilterType.Like`). - * @param value the value to compare the field against. - */ - public Criteria(String field, FilterType operator, Object value) { - this(field, operator, value, false); - } - - - /** - * Creates a basic criteria condition without negation. - */ - public static Criteria where(String field, FilterType operator, Object value) { - return new Criteria(field, operator, value, false); - } - - /** - * Creates a criteria condition with negation. - */ - public static Criteria not(String field, FilterType operator, Object value) { - return new Criteria(field, operator, value, true); - } - - /** - * Creates a LIKE criteria with a pattern, without negation. - */ - public static Criteria like(String field, String pattern) { - return new Criteria(field, FilterType.LIKE, pattern, false); - } - - /** - * Creates a BETWEEN criteria for range queries, without negation. - */ - public static Criteria between(String field, Object start, Object end) { - return new Criteria(field, FilterType.BETWEEN, List.of(start, end), false); - } -} diff --git a/api/src/main/java/jakarta/data/FilterType.java b/api/src/main/java/jakarta/data/Operator.java similarity index 58% rename from api/src/main/java/jakarta/data/FilterType.java rename to api/src/main/java/jakarta/data/Operator.java index 520dc30da..85ac77569 100644 --- a/api/src/main/java/jakarta/data/FilterType.java +++ b/api/src/main/java/jakarta/data/Operator.java @@ -19,58 +19,61 @@ /** - * Enum representing common filter types used to compare, match, or restrict values in Jakarta Data queries. - * The {@code FilterType} enum defines a set of operators to specify how values should be compared or matched - * in data filtering, providing flexibility for various querying needs across different contexts. + * Enum representing common operators used to define query conditions in Jakarta Data queries. + * The {@code Operator} enum provides a set of operations to specify how values should be compared, + * matched, or restricted when filtering data, supporting flexible and expressive querying across + * different contexts. */ -public enum FilterType { +public enum Operator { + /** * Matches records where the field value is exactly equal to the specified value. - * Often used for precise matching on identifiers, names, or other exact-value fields. + * Typically used for exact matching on unique identifiers, names, or other exact-value fields. */ EQUAL, /** * Matches records where the field value is greater than the specified value. - * Commonly used for numerical fields (e.g., dates, prices) to retrieve values above a threshold. + * Often applied to numerical fields (e.g., dates, prices) for retrieving values above a given threshold. */ GREATER_THAN, /** * Matches records where the field value is greater than or equal to the specified value. - * Useful for inclusive range queries to include a specified boundary. + * Useful for inclusive range queries, where the specified boundary is included in the results. */ GREATER_THAN_EQUAL, /** - * Filters records where the field value is contained within a specified collection or set of values. - * Often applied to match multiple values (e.g., category lists, status collections). + * Matches records where the field value is contained within a specified collection of values. + * Commonly used to match against multiple possible values, such as category or status lists. */ IN, /** * Matches records where the field value is less than the specified value. - * Frequently used in numerical comparisons to retrieve values below a threshold. + * Frequently used in numerical comparisons to retrieve values below a certain threshold. */ LESS_THAN, /** * Matches records where the field value is less than or equal to the specified value. - * Suitable for inclusive range queries that include the specified boundary. + * Suitable for inclusive range queries that include the specified boundary in the results. */ LESS_THAN_EQUAL, /** - * Matches records where the field value conforms to a specified pattern, often utilizing wildcards. - * Commonly applied to string fields, allowing partial matches (e.g., names containing a substring). + * Matches records where the field value conforms to a specified pattern, often with wildcards. + * Commonly applied to string fields for partial matches (e.g., names containing a substring). */ LIKE, /** - * Matches records where the field value falls within a specified range of values. - * Typically used for range comparisons, such as finding dates within a start and end period. + * Matches records where the field value falls within a specified range. + * Typically used for range-based comparisons, such as finding dates between a start and end date. */ BETWEEN; } + diff --git a/api/src/main/java/jakarta/data/Restriction.java b/api/src/main/java/jakarta/data/Restriction.java new file mode 100644 index 000000000..d6c4823f5 --- /dev/null +++ b/api/src/main/java/jakarta/data/Restriction.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +import java.util.List; + +/** + * Represents a restriction condition for repository queries, defining an entity's attribute, + * an operator, a comparison value, and an optional negation flag for flexible query construction. + * + *

Restrictions are used to specify filtering conditions, supporting type-safe attributes and + * a range of operators such as {@code EQUAL}, {@code LIKE}, {@code BETWEEN}, and more.

+ * + *

Example usage:

+ *
+ * Restriction titleRestriction = Restriction.like("title", "Data%");
+ * Restriction dateRestriction = Restriction.between("publicationDate", pastDate, LocalDate.now());
+ * 
+ * + * @param the entity type that this restriction applies to, ensuring type-safe usage. + */ +public record Restriction(String field, Operator operator, Object value, boolean negate) { + + /** + * Constructs a restriction condition without negation. + * + * @param field the name of the field to apply the restriction to (e.g., "title" or "publicationDate"). + * @param operator the operator defining the comparison or condition (e.g., `Operator.LIKE`). + * @param value the value to compare the field against. + */ + public Restriction(String field, Operator operator, Object value) { + this(field, operator, value, false); + } + + /** + * Creates a basic restriction condition without negation. + * + * @param field the field name of the entity. + * @param operator the operator defining the restriction type. + * @param value the value to apply in the restriction. + * @return a restriction condition. + */ + public static Restriction where(String field, Operator operator, Object value) { + return new Restriction<>(field, operator, value, false); + } + + /** + * Creates a restriction condition with negation. + * + * @param field the field name of the entity. + * @param operator the operator defining the restriction type. + * @param value the value to apply in the restriction. + * @return a negated restriction condition. + */ + public static Restriction not(String field, Operator operator, Object value) { + return new Restriction<>(field, operator, value, true); + } + + /** + * Creates a LIKE restriction with a pattern, without negation. + * + * @param field the field name of the entity. + * @param pattern the pattern to match. + * @return a LIKE restriction condition. + */ + public static Restriction like(String field, String pattern) { + return new Restriction<>(field, Operator.LIKE, pattern, false); + } + + /** + * Creates a BETWEEN restriction for range queries, without negation. + * + * @param field the field name of the entity. + * @param start the starting value of the range. + * @param end the ending value of the range. + * @return a BETWEEN restriction condition. + */ + public static Restriction between(String field, Object start, Object end) { + return new Restriction<>(field, Operator.BETWEEN, List.of(start, end), false); + } +} diff --git a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java index 2f7659380..3c200c6e8 100644 --- a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java @@ -17,7 +17,7 @@ */ package jakarta.data.metamodel; -import jakarta.data.Criteria; +import jakarta.data.Restriction; import java.util.Collection; @@ -48,7 +48,7 @@ public interface FilterableAttribute extends Attribute { * @param value the value to compare with the attribute. * @return a criteria condition representing "attribute = value". */ - Criteria equal(V value); + Restriction equal(V value); /** * Creates an inequality condition for the attribute, excluding the specified value. @@ -56,7 +56,7 @@ public interface FilterableAttribute extends Attribute { * @param value the value to exclude. * @return a criteria condition representing "attribute != value". */ - Criteria notEqual(V value); + Restriction notEqual(V value); /** * Creates a "like" condition for textual attributes, matching the specified pattern. @@ -65,7 +65,7 @@ public interface FilterableAttribute extends Attribute { * @param pattern the pattern to match, often using wildcards. * @return a criteria condition representing "attribute LIKE pattern". */ - Criteria like(String pattern); + Restriction like(String pattern); /** * Creates a "greater than" condition for the attribute, matching values greater than the specified value. @@ -73,7 +73,7 @@ public interface FilterableAttribute extends Attribute { * @param value the minimum value to match. * @return a criteria condition representing "attribute > value". */ - Criteria greaterThan(V value); + Restriction greaterThan(V value); /** * Creates a "greater than or equal to" condition for the attribute, matching values greater than or equal to the specified value. @@ -81,7 +81,7 @@ public interface FilterableAttribute extends Attribute { * @param value the minimum value to match inclusively. * @return a criteria condition representing "attribute >= value". */ - Criteria greaterThanOrEqual(V value); + Restriction greaterThanOrEqual(V value); /** * Creates a "less than" condition for the attribute, matching values less than the specified value. @@ -89,7 +89,7 @@ public interface FilterableAttribute extends Attribute { * @param value the maximum value to match. * @return a criteria condition representing "attribute < value". */ - Criteria lessThan(V value); + Restriction lessThan(V value); /** * Creates a "less than or equal to" condition for the attribute, matching values less than or equal to the specified value. @@ -97,7 +97,7 @@ public interface FilterableAttribute extends Attribute { * @param value the maximum value to match inclusively. * @return a criteria condition representing "attribute <= value". */ - Criteria lessThanOrEqual(V value); + Restriction lessThanOrEqual(V value); /** * Creates a "between" condition for the attribute, matching values within the specified range. @@ -106,7 +106,7 @@ public interface FilterableAttribute extends Attribute { * @param end the upper bound of the range. * @return a criteria condition representing "attribute BETWEEN start AND end". */ - Criteria between(V start, V end); + Restriction between(V start, V end); /** * Creates an "in" condition for the attribute, matching any of the specified values. @@ -114,5 +114,5 @@ public interface FilterableAttribute extends Attribute { * @param values the collection of values to match. * @return a criteria condition representing "attribute IN (values)". */ - Criteria in(Collection values); + Restriction in(Collection values); } diff --git a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java index 279bbeb5f..01c6f2bbf 100644 --- a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java +++ b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java @@ -17,7 +17,7 @@ */ package jakarta.data.repository; -import jakarta.data.Criteria; +import jakarta.data.Restriction; import jakarta.data.page.Page; import jakarta.data.page.PageRequest; @@ -25,7 +25,7 @@ /** * Repository interface that extends {@link DataRepository} to add support for dynamic filtering - * using {@link Criteria} objects with optional pagination. + * using {@link Restriction} objects with optional pagination. * *

This repository allows flexible querying by accepting varargs of criteria conditions * for filtering and supports pagination to retrieve results in pages or as a full list.

@@ -63,7 +63,7 @@ public interface CriteriaRepository extends DataRepository { * @param criteria the varargs of criteria conditions to filter the results. * @return a page of entities that match the criteria. */ - Page filter(PageRequest pageRequest, Criteria... criteria); + Page filter(PageRequest pageRequest, Restriction... criteria); /** * Finds all entities by applying the specified criteria conditions, returning results as a list without pagination. @@ -71,7 +71,7 @@ public interface CriteriaRepository extends DataRepository { * @param criteria the varargs of criteria conditions to filter the results. * @return a list of entities that match the criteria. */ - List filter(Criteria... criteria); + List filter(Restriction... criteria); /** * Counts entities by applying the specified criteria conditions. @@ -79,5 +79,5 @@ public interface CriteriaRepository extends DataRepository { * @param criteria the varargs of criteria conditions to filter the results. * @return the count of entities that match the criteria. */ - long countBy(Criteria... criteria); + long countBy(Restriction... criteria); } diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java index 251904f6e..e02142096 100644 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -17,11 +17,11 @@ */ package jakarta.data.repository; -import jakarta.data.FilterType; +import jakarta.data.Operator; /** * Annotation to specify query conditions on method parameters in Jakarta Data repository interfaces. - * The {@code @Is} annotation, combined with the {@link FilterType} enum, enables flexible and expressive + * The {@code @Is} annotation, combined with the {@link Operator} enum, enables flexible and expressive * filtering in repository queries. * Usage Example: *
{@code
@@ -42,13 +42,13 @@
     /**
      * Defines the condition to apply to the annotated parameter.
      *
-     * @return the specified {@link FilterType} value.
+     * @return the specified {@link Operator} value.
      */
-    FilterType value();
+    Operator value();
 
     /**
      * When set to true, applies the negation of the specified condition.
-     * For example, using `not = true` with {@link FilterType#Equal} will search for records
+     * For example, using `not = true` with {@link Operator#Equal} will search for records
      * where the field is not equal to the specified value.
      *
      * @return whether to negate the specified condition.

From 592a1004a65e5aad30b2b734b4251ae6cde95e42 Mon Sep 17 00:00:00 2001
From: Otavio Santana 
Date: Wed, 6 Nov 2024 03:32:09 +0000
Subject: [PATCH 05/57] feat: include logical operator

Signed-off-by: Otavio Santana 
---
 api/src/main/java/jakarta/data/LogicalOperator.java | 9 +++++++++
 api/src/main/java/jakarta/data/repository/Is.java   | 4 ++--
 2 files changed, 11 insertions(+), 2 deletions(-)
 create mode 100644 api/src/main/java/jakarta/data/LogicalOperator.java

diff --git a/api/src/main/java/jakarta/data/LogicalOperator.java b/api/src/main/java/jakarta/data/LogicalOperator.java
new file mode 100644
index 000000000..7688e8735
--- /dev/null
+++ b/api/src/main/java/jakarta/data/LogicalOperator.java
@@ -0,0 +1,9 @@
+package jakarta.data;
+
+/**
+ * Enum representing logical operators for combining multiple {@link Restriction} objects.
+ */
+public enum LogicalOperator {
+    AND,
+    OR
+}
diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java
index e02142096..5e5bf3885 100644
--- a/api/src/main/java/jakarta/data/repository/Is.java
+++ b/api/src/main/java/jakarta/data/repository/Is.java
@@ -26,14 +26,14 @@
  * Usage Example:
  * 
{@code
  * @Find
- * Page search(@Is(Conditions.Equal) String name); // Finds products where the name matches exactly.
+ * Page search(@Is(Operator.Equal) String name); // Finds products where the name matches exactly.
  * }
* * Example for Negation: * *
{@code
  * @Find
- * Page search(@Is(value = Conditions.Equal, not = true) String excludedName); // Excludes products with the specified name.
+ * Page search(@Is(value = Operator.Equal, not = true) String excludedName); // Excludes products with the specified name.
  * }
* */ From 4cb44d3773e0a444a62ea124193076164995fb15 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 03:42:27 +0000 Subject: [PATCH 06/57] feat: create restriction proposal Signed-off-by: Otavio Santana --- .../jakarta/data/CompositeRestriction.java | 51 +++++++++ .../data/repository/CriteriaRepository.java | 83 -------------- .../repository/RestrictionRepository.java | 107 ++++++++++++++++++ 3 files changed, 158 insertions(+), 83 deletions(-) create mode 100644 api/src/main/java/jakarta/data/CompositeRestriction.java delete mode 100644 api/src/main/java/jakarta/data/repository/CriteriaRepository.java create mode 100644 api/src/main/java/jakarta/data/repository/RestrictionRepository.java diff --git a/api/src/main/java/jakarta/data/CompositeRestriction.java b/api/src/main/java/jakarta/data/CompositeRestriction.java new file mode 100644 index 000000000..a55960e2f --- /dev/null +++ b/api/src/main/java/jakarta/data/CompositeRestriction.java @@ -0,0 +1,51 @@ +package jakarta.data; + +import java.util.List; + +/** + * A composite restriction representing a collection of individual {@link Restriction} + * and {@link LogicalOperator} instances, combined under a single logical operation. + * + *

This record allows multiple restrictions to be treated as a single entity, making + * it easy to pass complex conditions to repository methods.

+ * + * @param the entity type that the restrictions apply to. + */ +public record CompositeRestriction(LogicalOperator operator, List> restrictions) { + + /** + * Constructs a composite restriction with the specified operator and list of restrictions. + * + * @param operator the logical operator (AND or OR) to apply between the restrictions. + * @param restrictions the list of restrictions to combine. + */ + public CompositeRestriction { + restrictions = List.copyOf(restrictions); // Ensure immutability of the list + } + + // Factory methods for creating composite restrictions with AND or OR logic + + /** + * Creates a composite restriction where all specified restrictions must be true (AND logic). + * + * @param restrictions the individual restrictions to combine. + * @param the entity type that the restrictions apply to. + * @return a CompositeRestriction representing the AND combination of the provided restrictions. + */ + @SafeVarargs + public static CompositeRestriction all(Restriction... restrictions) { + return new CompositeRestriction<>(LogicalOperator.AND, List.of(restrictions)); + } + + /** + * Creates a composite restriction where any of the specified restrictions may be true (OR logic). + * + * @param restrictions the individual restrictions to combine. + * @param the entity type that the restrictions apply to. + * @return a CompositeRestriction representing the OR combination of the provided restrictions. + */ + @SafeVarargs + public static CompositeRestriction any(Restriction... restrictions) { + return new CompositeRestriction<>(LogicalOperator.OR, List.of(restrictions)); + } +} diff --git a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java b/api/src/main/java/jakarta/data/repository/CriteriaRepository.java deleted file mode 100644 index 01c6f2bbf..000000000 --- a/api/src/main/java/jakarta/data/repository/CriteriaRepository.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data.repository; - -import jakarta.data.Restriction; -import jakarta.data.page.Page; -import jakarta.data.page.PageRequest; - -import java.util.List; - -/** - * Repository interface that extends {@link DataRepository} to add support for dynamic filtering - * using {@link Restriction} objects with optional pagination. - * - *

This repository allows flexible querying by accepting varargs of criteria conditions - * for filtering and supports pagination to retrieve results in pages or as a full list.

- * - *

Example usage:

- * - *
- * @Inject
- * DriverLicenses licenses;
- *
- * // Define and apply criteria using metamodel attributes
- * PageRequest pageRequest = PageRequest.of(0, 10);
- * Page pagedResults = licenses.findByCriteria(pageRequest,
- *     _DriverLicense.license.like("ABC%"),
- *     _DriverLicense.expiry.greaterThan(LocalDate.now())
- * );
- *
- * // Fetch results without pagination
- * List allResults = licenses.findByCriteria(
- *     _DriverLicense.license.like("ABC%"),
- *     _DriverLicense.expiry.greaterThan(LocalDate.now())
- * );
- * 
- * - * @param the type of the primary entity class of the repository. - * @param the type of the unique identifier field or property of the primary entity. - */ -public interface CriteriaRepository extends DataRepository { - - - /** - * Finds entities by applying the specified criteria conditions, with support for pagination. - * - * @param pageRequest the pagination information including page number and size. - * @param criteria the varargs of criteria conditions to filter the results. - * @return a page of entities that match the criteria. - */ - Page filter(PageRequest pageRequest, Restriction... criteria); - - /** - * Finds all entities by applying the specified criteria conditions, returning results as a list without pagination. - * - * @param criteria the varargs of criteria conditions to filter the results. - * @return a list of entities that match the criteria. - */ - List filter(Restriction... criteria); - - /** - * Counts entities by applying the specified criteria conditions. - * - * @param criteria the varargs of criteria conditions to filter the results. - * @return the count of entities that match the criteria. - */ - long countBy(Restriction... criteria); -} diff --git a/api/src/main/java/jakarta/data/repository/RestrictionRepository.java b/api/src/main/java/jakarta/data/repository/RestrictionRepository.java new file mode 100644 index 000000000..1614b4d1f --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/RestrictionRepository.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.repository; + +import jakarta.data.CompositeRestriction; +import jakarta.data.Restriction; +import jakarta.data.Sort; +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; + +import java.util.List; +/** + * Repository interface that supports advanced filtering using {@link Restriction} and + * {@link CompositeRestriction}, along with type-safe sorting capabilities using {@link Sort}. + * This enables developers to construct complex queries with multiple restrictions, + * logical operations, sorting, and pagination. + * + *

The {@code RestrictionRepository} provides flexible query methods that accept both + * single and composite restrictions, allowing for various filter conditions with type-safe sorting.

+ * + *

Example usage:

+ *
+ * @Inject
+ * DriverLicenses licenses;
+ *
+ * // Define individual restrictions
+ * Restriction<DriverLicense> licenseNumRestriction = Restriction.like("licenseNum", "ABC%");
+ * Restriction<DriverLicense> expiryRestriction = Restriction.greaterThan("expiry", LocalDate.now());
+ * Restriction<DriverLicense> regionRestriction = Restriction.equal("region", "North");
+ *
+ * // Combine restrictions with AND logic using CompositeRestriction.all
+ * CompositeRestriction<DriverLicense> andRestriction = CompositeRestriction.all(
+ *     licenseNumRestriction,
+ *     expiryRestriction,
+ *     regionRestriction
+ * );
+ *
+ * // Define type-safe sorting criteria
+ * Sort<DriverLicense> sortByExpiry = Sort.asc("expiry");
+ * Sort<DriverLicense> sortByLicenseNumDesc = Sort.desc("licenseNum");
+ *
+ * // Use the composite restriction with sorting criteria
+ * List<DriverLicense> sortedResults = licenses.findByRestriction(andRestriction, sortByExpiry, sortByLicenseNumDesc);
+ *
+ * // Paginated query with composite restriction and sorting
+ * PageRequest pageRequest = PageRequest.of(0, 10);
+ * Page<DriverLicense> pagedResults = licenses.findByRestriction(andRestriction, pageRequest, sortByExpiry, sortByLicenseNumDesc);
+ *
+ * // Count entities with a single restriction
+ * long count = licenses.countByRestriction(expiryRestriction);
+ * 
+ * + * @param the type of the primary entity class of the repository. + * @param the type of the unique identifier field or property of the primary entity. + */ +public interface RestrictionRepository extends DataRepository { + + /** + * Finds entities by applying the specified composite restriction, with pagination and sorting. + * + * @param restriction the composite restriction containing filter conditions. + * @param pageRequest the pagination information. + * @param sorts the sorting criteria. + * @return a page of entities that match the provided restriction criteria, sorted as specified. + */ + Page findByRestriction(CompositeRestriction restriction, PageRequest pageRequest, Sort... sorts); + + /** + * Finds all entities by applying the specified composite restriction, with sorting but without pagination. + * + * @param restriction the composite restriction containing filter conditions. + * @param sorts the sorting criteria. + * @return a list of entities that match the provided restriction criteria, ordered by the specified fields. + */ + List findByRestriction(CompositeRestriction restriction, Sort... sorts); + + /** + * Finds all entities by applying the specified composite restriction without pagination or sorting. + * + * @param restriction the composite restriction containing filter conditions. + * @return a list of entities that match the provided restriction criteria. + */ + List findByRestriction(CompositeRestriction restriction); + + /** + * Counts entities by applying the specified restriction. + * + * @param restriction the restriction containing filter conditions. + * @return the count of entities that match the restriction criteria. + */ + long countByRestriction(Restriction restriction); +} From 8b8898f7cd4520dda07f7957152f3667322ea16c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 03:45:47 +0000 Subject: [PATCH 07/57] feat: create and update queries at RestrictionRepository Signed-off-by: Otavio Santana --- .../repository/RestrictionRepository.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/jakarta/data/repository/RestrictionRepository.java b/api/src/main/java/jakarta/data/repository/RestrictionRepository.java index 1614b4d1f..591ae8594 100644 --- a/api/src/main/java/jakarta/data/repository/RestrictionRepository.java +++ b/api/src/main/java/jakarta/data/repository/RestrictionRepository.java @@ -24,9 +24,10 @@ import jakarta.data.page.PageRequest; import java.util.List; + /** * Repository interface that supports advanced filtering using {@link Restriction} and - * {@link CompositeRestriction}, along with type-safe sorting capabilities using {@link Sort}. + * {@link CompositeRestriction}, along with type-safe sorting capabilities using the static metamodel. * This enables developers to construct complex queries with multiple restrictions, * logical operations, sorting, and pagination. * @@ -38,28 +39,28 @@ * @Inject * DriverLicenses licenses; * - * // Define individual restrictions - * Restriction<DriverLicense> licenseNumRestriction = Restriction.like("licenseNum", "ABC%"); - * Restriction<DriverLicense> expiryRestriction = Restriction.greaterThan("expiry", LocalDate.now()); - * Restriction<DriverLicense> regionRestriction = Restriction.equal("region", "North"); + * // Define individual restrictions using the static metamodel for type-safe attribute references + * Restriction<DriverLicense> licenseRestriction = _DriverLicense.license.like("ABC%"); + * Restriction<DriverLicense> expiryRestriction = _DriverLicense.expiry.greaterThan(LocalDate.now()); + * Restriction<DriverLicense> regionRestriction = _DriverLicense.region.equal("North"); * * // Combine restrictions with AND logic using CompositeRestriction.all * CompositeRestriction<DriverLicense> andRestriction = CompositeRestriction.all( - * licenseNumRestriction, + * licenseRestriction, * expiryRestriction, * regionRestriction * ); * - * // Define type-safe sorting criteria - * Sort<DriverLicense> sortByExpiry = Sort.asc("expiry"); - * Sort<DriverLicense> sortByLicenseNumDesc = Sort.desc("licenseNum"); + * // Define type-safe sorting criteria using the static metamodel + * Sort<DriverLicense> sortByExpiry = _DriverLicense.expiry.asc(); + * Sort<DriverLicense> sortByLicenseDesc = _DriverLicense.license.desc(); * * // Use the composite restriction with sorting criteria - * List<DriverLicense> sortedResults = licenses.findByRestriction(andRestriction, sortByExpiry, sortByLicenseNumDesc); + * List<DriverLicense> sortedResults = licenses.findByRestriction(andRestriction, sortByExpiry, sortByLicenseDesc); * * // Paginated query with composite restriction and sorting * PageRequest pageRequest = PageRequest.of(0, 10); - * Page<DriverLicense> pagedResults = licenses.findByRestriction(andRestriction, pageRequest, sortByExpiry, sortByLicenseNumDesc); + * Page<DriverLicense> pagedResults = licenses.findByRestriction(andRestriction, pageRequest, sortByExpiry, sortByLicenseDesc); * * // Count entities with a single restriction * long count = licenses.countByRestriction(expiryRestriction); From 8ceb3a5761feb70edac22d555e0d72636843476a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 03:48:05 +0000 Subject: [PATCH 08/57] style: inclulde header license Signed-off-by: Otavio Santana --- .../java/jakarta/data/CompositeRestriction.java | 17 +++++++++++++++++ .../main/java/jakarta/data/LogicalOperator.java | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/api/src/main/java/jakarta/data/CompositeRestriction.java b/api/src/main/java/jakarta/data/CompositeRestriction.java index a55960e2f..873f6a68e 100644 --- a/api/src/main/java/jakarta/data/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/CompositeRestriction.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package jakarta.data; import java.util.List; diff --git a/api/src/main/java/jakarta/data/LogicalOperator.java b/api/src/main/java/jakarta/data/LogicalOperator.java index 7688e8735..0e82c8035 100644 --- a/api/src/main/java/jakarta/data/LogicalOperator.java +++ b/api/src/main/java/jakarta/data/LogicalOperator.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package jakarta.data; /** From ef4fd7ba4783a372a8efefd1eec3ca1ed96dd9bb Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 15:22:09 +0000 Subject: [PATCH 09/57] feat: update structure based on the discussion on meeting today Signed-off-by: Otavio Santana --- .../main/java/jakarta/data/Restriction.java | 34 ++--- .../data/metamodel/FilterableAttribute.java | 108 +++++++--------- .../main/java/jakarta/data/repository/Is.java | 16 --- .../java/jakarta/data/repository/Pattern.java | 116 ++++++++++++++++++ .../repository/RestrictionRepository.java | 108 ---------------- 5 files changed, 173 insertions(+), 209 deletions(-) create mode 100644 api/src/main/java/jakarta/data/repository/Pattern.java delete mode 100644 api/src/main/java/jakarta/data/repository/RestrictionRepository.java diff --git a/api/src/main/java/jakarta/data/Restriction.java b/api/src/main/java/jakarta/data/Restriction.java index d6c4823f5..ce9e7da41 100644 --- a/api/src/main/java/jakarta/data/Restriction.java +++ b/api/src/main/java/jakarta/data/Restriction.java @@ -21,7 +21,7 @@ /** * Represents a restriction condition for repository queries, defining an entity's attribute, - * an operator, a comparison value, and an optional negation flag for flexible query construction. + * an operator, and a comparison value for flexible query construction. * *

Restrictions are used to specify filtering conditions, supporting type-safe attributes and * a range of operators such as {@code EQUAL}, {@code LIKE}, {@code BETWEEN}, and more.

@@ -34,21 +34,21 @@ * * @param the entity type that this restriction applies to, ensuring type-safe usage. */ -public record Restriction(String field, Operator operator, Object value, boolean negate) { +public record Restriction(String field, Operator operator, Object value) { /** - * Constructs a restriction condition without negation. + * Constructs a restriction condition. * * @param field the name of the field to apply the restriction to (e.g., "title" or "publicationDate"). * @param operator the operator defining the comparison or condition (e.g., `Operator.LIKE`). * @param value the value to compare the field against. */ - public Restriction(String field, Operator operator, Object value) { - this(field, operator, value, false); + public Restriction { + // Validation or processing logic can go here if needed } /** - * Creates a basic restriction condition without negation. + * Creates a basic restriction condition. * * @param field the field name of the entity. * @param operator the operator defining the restriction type. @@ -56,34 +56,22 @@ public Restriction(String field, Operator operator, Object value) { * @return a restriction condition. */ public static Restriction where(String field, Operator operator, Object value) { - return new Restriction<>(field, operator, value, false); + return new Restriction<>(field, operator, value); } /** - * Creates a restriction condition with negation. - * - * @param field the field name of the entity. - * @param operator the operator defining the restriction type. - * @param value the value to apply in the restriction. - * @return a negated restriction condition. - */ - public static Restriction not(String field, Operator operator, Object value) { - return new Restriction<>(field, operator, value, true); - } - - /** - * Creates a LIKE restriction with a pattern, without negation. + * Creates a LIKE restriction with a pattern. * * @param field the field name of the entity. * @param pattern the pattern to match. * @return a LIKE restriction condition. */ public static Restriction like(String field, String pattern) { - return new Restriction<>(field, Operator.LIKE, pattern, false); + return new Restriction<>(field, Operator.LIKE, pattern); } /** - * Creates a BETWEEN restriction for range queries, without negation. + * Creates a BETWEEN restriction for range queries. * * @param field the field name of the entity. * @param start the starting value of the range. @@ -91,6 +79,6 @@ public static Restriction like(String field, String pattern) { * @return a BETWEEN restriction condition. */ public static Restriction between(String field, Object start, Object end) { - return new Restriction<>(field, Operator.BETWEEN, List.of(start, end), false); + return new Restriction<>(field, Operator.BETWEEN, List.of(start, end)); } } diff --git a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java index 3c200c6e8..cf214568e 100644 --- a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java @@ -19,100 +19,84 @@ import jakarta.data.Restriction; -import java.util.Collection; +import java.util.function.Supplier; /** - * Represents an entity attribute in the {@link StaticMetamodel} that supports filtering conditions. - * This interface provides methods for defining common query conditions, allowing the attribute to be used - * in filtering expressions such as `_Book.title.like("Jakarta Data%")`. + * Represents an entity attribute that supports filtering operations in repository queries, + * including type-safe comparisons, range restrictions, and pattern matching (e.g., LIKE queries). * - *

Example usage:

- *
{@code
- * // Define criteria for filtering
- * Criteria titleCriteria = _Book.title.like("Jakarta Data%");
- * Criteria dateCriteria = _Book.publicationDate.between(pastDate, LocalDate.now());
- * Criteria authorCriteria = _Book.author.equal("John Doe");
+ * 

The `FilterableAttribute` interface provides methods for creating various filtering + * restrictions, enabling expressive and type-safe query construction for attributes + * such as numeric values, dates, and strings.

* - * // Apply criteria in a repository query - * repo.books(List.of(titleCriteria, dateCriteria, authorCriteria)); - * }
+ *

Example usage:

+ *
+ * // Define filtering conditions on entity attributes
+ * Restriction titleRestriction = _Book.title.like(Pattern.prefixedIgnoreCase("Jak"));
+ * Restriction dateRestriction = _Book.publicationDate.between(pastDate, LocalDate.now());
+ * 
* - * @param the type of the entity class in the static metamodel. - * @param the type of the attribute's value. + * @param the entity type that this attribute belongs to. */ -public interface FilterableAttribute extends Attribute { - - /** - * Creates an equality condition for the attribute, matching the specified value. - * - * @param value the value to compare with the attribute. - * @return a criteria condition representing "attribute = value". - */ - Restriction equal(V value); - - /** - * Creates an inequality condition for the attribute, excluding the specified value. - * - * @param value the value to exclude. - * @return a criteria condition representing "attribute != value". - */ - Restriction notEqual(V value); +public interface FilterableAttribute { /** - * Creates a "like" condition for textual attributes, matching the specified pattern. - * This is typically used for partial matches in string fields. + * Creates an equality restriction for the attribute. * - * @param pattern the pattern to match, often using wildcards. - * @return a criteria condition representing "attribute LIKE pattern". + * @param value the value to match exactly. + * @return a Restriction representing an equality condition. */ - Restriction like(String pattern); + Restriction equal(Object value); /** - * Creates a "greater than" condition for the attribute, matching values greater than the specified value. + * Creates a restriction for values greater than the specified value. * - * @param value the minimum value to match. - * @return a criteria condition representing "attribute > value". + * @param value the lower bound (exclusive) for the attribute. + * @return a Restriction representing a greater-than condition. */ - Restriction greaterThan(V value); + Restriction greaterThan(Object value); /** - * Creates a "greater than or equal to" condition for the attribute, matching values greater than or equal to the specified value. + * Creates a restriction for values greater than or equal to the specified value. * - * @param value the minimum value to match inclusively. - * @return a criteria condition representing "attribute >= value". + * @param value the lower bound (inclusive) for the attribute. + * @return a Restriction representing a greater-than-or-equal condition. */ - Restriction greaterThanOrEqual(V value); + Restriction greaterThanOrEqual(Object value); /** - * Creates a "less than" condition for the attribute, matching values less than the specified value. + * Creates a restriction for values less than the specified value. * - * @param value the maximum value to match. - * @return a criteria condition representing "attribute < value". + * @param value the upper bound (exclusive) for the attribute. + * @return a Restriction representing a less-than condition. */ - Restriction lessThan(V value); + Restriction lessThan(Object value); /** - * Creates a "less than or equal to" condition for the attribute, matching values less than or equal to the specified value. + * Creates a restriction for values less than or equal to the specified value. * - * @param value the maximum value to match inclusively. - * @return a criteria condition representing "attribute <= value". + * @param value the upper bound (inclusive) for the attribute. + * @return a Restriction representing a less-than-or-equal condition. */ - Restriction lessThanOrEqual(V value); + Restriction lessThanOrEqual(Object value); /** - * Creates a "between" condition for the attribute, matching values within the specified range. + * Creates a restriction that matches values within the specified range. * - * @param start the lower bound of the range. - * @param end the upper bound of the range. - * @return a criteria condition representing "attribute BETWEEN start AND end". + * @param start the starting value of the range (inclusive). + * @param end the ending value of the range (inclusive). + * @return a Restriction representing a range condition. */ - Restriction between(V start, V end); + Restriction between(Object start, Object end); /** - * Creates an "in" condition for the attribute, matching any of the specified values. + * Creates a `LIKE` restriction using a `Pattern` for the attribute, + * supporting different `LIKE` options such as prefix, suffix, and substring matching. * - * @param values the collection of values to match. - * @return a criteria condition representing "attribute IN (values)". + * @param pattern the pattern to match, defined using the `Pattern` class. + * @return a Restriction representing the `LIKE` condition. */ - Restriction in(Collection values); + default Restriction like(Supplier> pattern) { + return pattern.get(); + } } diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java index 5e5bf3885..b06de2880 100644 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -29,13 +29,6 @@ * Page search(@Is(Operator.Equal) String name); // Finds products where the name matches exactly. * }
* - * Example for Negation: - * - *
{@code
- * @Find
- * Page search(@Is(value = Operator.Equal, not = true) String excludedName); // Excludes products with the specified name.
- * }
- * */ public @interface Is { @@ -45,13 +38,4 @@ * @return the specified {@link Operator} value. */ Operator value(); - - /** - * When set to true, applies the negation of the specified condition. - * For example, using `not = true` with {@link Operator#Equal} will search for records - * where the field is not equal to the specified value. - * - * @return whether to negate the specified condition. - */ - boolean not() default false; } diff --git a/api/src/main/java/jakarta/data/repository/Pattern.java b/api/src/main/java/jakarta/data/repository/Pattern.java new file mode 100644 index 000000000..322daf50f --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/Pattern.java @@ -0,0 +1,116 @@ +package jakarta.data.repository; + +import jakarta.data.Operator; +import jakarta.data.Restriction; + +import java.util.function.Supplier; +/** + * Represents a pattern for SQL `LIKE` operations, encapsulating different + * `LIKE` matching options, such as prefix, suffix, and substring matching, + * and supplying a `Restriction` for easy use in repository queries. + * + *

This class supports type-safe attributes and various matching types, + * including case-insensitive patterns.

+ * + * @param the type of the entity on which the restriction will be applied. + */ +public record Pattern(String field, String value, boolean ignoreCase) implements Supplier> { + + /** + * Supplies a `Restriction` configured with this `LIKE` pattern. + * + * @return a `Restriction` representing the `LIKE` pattern. + */ + @Override + public Restriction get() { + return new Restriction<>(field, Operator.LIKE, value); + } + + /** + * Creates a case-sensitive `LIKE` pattern for an exact match. + * + * @param field the field to apply the pattern on. + * @param value the value to match exactly. + * @return a Pattern for an exact match. + */ + public static Pattern like(String field, String value) { + return new Pattern<>(field, value, false); + } + + /** + * Creates a case-insensitive `LIKE` pattern for an exact match. + * + * @param field the field to apply the pattern on. + * @param value the value to match exactly. + * @return a Pattern for a case-insensitive exact match. + */ + public static Pattern likeIgnoreCase(String field, String value) { + return new Pattern<>(field, value, true); + } + + /** + * Creates a `LIKE` pattern for values prefixed with the specified value. + * + * @param field the field to apply the pattern on. + * @param value the prefix to match. + * @return a Pattern for prefix matching. + */ + public static Pattern prefixed(String field, String value) { + return new Pattern<>(field, value + "%", false); + } + + /** + * Creates a case-insensitive `LIKE` pattern for values prefixed with the specified value. + * + * @param field the field to apply the pattern on. + * @param value the prefix to match. + * @return a Pattern for case-insensitive prefix matching. + */ + public static Pattern prefixedIgnoreCase(String field, String value) { + return new Pattern<>(field, value + "%", true); + } + + /** + * Creates a `LIKE` pattern for values containing the specified substring. + * + * @param field the field to apply the pattern on. + * @param value the substring to match. + * @return a Pattern for substring matching. + */ + public static Pattern substringed(String field, String value) { + return new Pattern<>(field, "%" + value + "%", false); + } + + /** + * Creates a case-insensitive `LIKE` pattern for values containing the specified substring. + * + * @param field the field to apply the pattern on. + * @param value the substring to match. + * @return a Pattern for case-insensitive substring matching. + */ + public static Pattern substringedIgnoreCase(String field, String value) { + return new Pattern<>(field, "%" + value + "%", true); + } + + /** + * Creates a `LIKE` pattern for values suffixed with the specified value. + * + * @param field the field to apply the pattern on. + * @param value the suffix to match. + * @return a Pattern for suffix matching. + */ + public static Pattern suffixed(String field, String value) { + return new Pattern<>(field, "%" + value, false); + } + + /** + * Creates a case-insensitive `LIKE` pattern for values suffixed with the specified value. + * + * @param field the field to apply the pattern on. + * @param value the suffix to match. + * @return a Pattern for case-insensitive suffix matching. + */ + public static Pattern suffixedIgnoreCase(String field, String value) { + return new Pattern<>(field, "%" + value, true); + } +} diff --git a/api/src/main/java/jakarta/data/repository/RestrictionRepository.java b/api/src/main/java/jakarta/data/repository/RestrictionRepository.java deleted file mode 100644 index 591ae8594..000000000 --- a/api/src/main/java/jakarta/data/repository/RestrictionRepository.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data.repository; - -import jakarta.data.CompositeRestriction; -import jakarta.data.Restriction; -import jakarta.data.Sort; -import jakarta.data.page.Page; -import jakarta.data.page.PageRequest; - -import java.util.List; - -/** - * Repository interface that supports advanced filtering using {@link Restriction} and - * {@link CompositeRestriction}, along with type-safe sorting capabilities using the static metamodel. - * This enables developers to construct complex queries with multiple restrictions, - * logical operations, sorting, and pagination. - * - *

The {@code RestrictionRepository} provides flexible query methods that accept both - * single and composite restrictions, allowing for various filter conditions with type-safe sorting.

- * - *

Example usage:

- *
- * @Inject
- * DriverLicenses licenses;
- *
- * // Define individual restrictions using the static metamodel for type-safe attribute references
- * Restriction<DriverLicense> licenseRestriction = _DriverLicense.license.like("ABC%");
- * Restriction<DriverLicense> expiryRestriction = _DriverLicense.expiry.greaterThan(LocalDate.now());
- * Restriction<DriverLicense> regionRestriction = _DriverLicense.region.equal("North");
- *
- * // Combine restrictions with AND logic using CompositeRestriction.all
- * CompositeRestriction<DriverLicense> andRestriction = CompositeRestriction.all(
- *     licenseRestriction,
- *     expiryRestriction,
- *     regionRestriction
- * );
- *
- * // Define type-safe sorting criteria using the static metamodel
- * Sort<DriverLicense> sortByExpiry = _DriverLicense.expiry.asc();
- * Sort<DriverLicense> sortByLicenseDesc = _DriverLicense.license.desc();
- *
- * // Use the composite restriction with sorting criteria
- * List<DriverLicense> sortedResults = licenses.findByRestriction(andRestriction, sortByExpiry, sortByLicenseDesc);
- *
- * // Paginated query with composite restriction and sorting
- * PageRequest pageRequest = PageRequest.of(0, 10);
- * Page<DriverLicense> pagedResults = licenses.findByRestriction(andRestriction, pageRequest, sortByExpiry, sortByLicenseDesc);
- *
- * // Count entities with a single restriction
- * long count = licenses.countByRestriction(expiryRestriction);
- * 
- * - * @param the type of the primary entity class of the repository. - * @param the type of the unique identifier field or property of the primary entity. - */ -public interface RestrictionRepository extends DataRepository { - - /** - * Finds entities by applying the specified composite restriction, with pagination and sorting. - * - * @param restriction the composite restriction containing filter conditions. - * @param pageRequest the pagination information. - * @param sorts the sorting criteria. - * @return a page of entities that match the provided restriction criteria, sorted as specified. - */ - Page findByRestriction(CompositeRestriction restriction, PageRequest pageRequest, Sort... sorts); - - /** - * Finds all entities by applying the specified composite restriction, with sorting but without pagination. - * - * @param restriction the composite restriction containing filter conditions. - * @param sorts the sorting criteria. - * @return a list of entities that match the provided restriction criteria, ordered by the specified fields. - */ - List findByRestriction(CompositeRestriction restriction, Sort... sorts); - - /** - * Finds all entities by applying the specified composite restriction without pagination or sorting. - * - * @param restriction the composite restriction containing filter conditions. - * @return a list of entities that match the provided restriction criteria. - */ - List findByRestriction(CompositeRestriction restriction); - - /** - * Counts entities by applying the specified restriction. - * - * @param restriction the restriction containing filter conditions. - * @return the count of entities that match the restriction criteria. - */ - long countByRestriction(Restriction restriction); -} From ee8b4fdf89ea0500e281f1229928ec93edc1f60a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 15:45:09 +0000 Subject: [PATCH 10/57] refact: convert restriction to interface Signed-off-by: Otavio Santana --- .../java/jakarta/data/BasicRestriction.java | 40 +++++ api/src/main/java/jakarta/data/Operator.java | 11 +- .../main/java/jakarta/data/Restriction.java | 157 +++++++++++++----- .../jakarta/data/metamodel/Attribute.java | 16 ++ .../data/metamodel/SortableAttribute.java | 42 +++++ .../jakarta/data/metamodel/TextAttribute.java | 14 ++ .../java/jakarta/data/repository/Pattern.java | 59 ++++--- 7 files changed, 281 insertions(+), 58 deletions(-) create mode 100644 api/src/main/java/jakarta/data/BasicRestriction.java diff --git a/api/src/main/java/jakarta/data/BasicRestriction.java b/api/src/main/java/jakarta/data/BasicRestriction.java new file mode 100644 index 000000000..ac760b7c5 --- /dev/null +++ b/api/src/main/java/jakarta/data/BasicRestriction.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +/** + * A basic implementation of the `Restriction` interface, representing various conditions + * used in repository queries, including equality, comparison, and range checks. + * + * @param the type of the entity on which the restriction is applied. + */ +public record BasicRestriction(String field, Operator operator, Object value) implements Restriction { + + /** + * Constructs a `BasicRestriction` with the specified field, operator, and value. + * + * @param field the name of the field to apply the restriction to. + * @param operator the operator defining the comparison or condition. + * @param value the value to compare the field against (optional for null checks). + */ + public BasicRestriction { + if (field == null || operator == null) { + throw new IllegalArgumentException("Field and operator must not be null."); + } + } +} diff --git a/api/src/main/java/jakarta/data/Operator.java b/api/src/main/java/jakarta/data/Operator.java index 85ac77569..748cb0451 100644 --- a/api/src/main/java/jakarta/data/Operator.java +++ b/api/src/main/java/jakarta/data/Operator.java @@ -72,7 +72,16 @@ public enum Operator { * Matches records where the field value falls within a specified range. * Typically used for range-based comparisons, such as finding dates between a start and end date. */ - BETWEEN; + BETWEEN, + + /** + * Matches records where the field value is null. + * + *

This operator is used to filter records where the specified field + * does not contain a value. Commonly used to find records with unset or + * missing data in a particular field.

+ */ + IS_NULL } diff --git a/api/src/main/java/jakarta/data/Restriction.java b/api/src/main/java/jakarta/data/Restriction.java index ce9e7da41..3be8b995b 100644 --- a/api/src/main/java/jakarta/data/Restriction.java +++ b/api/src/main/java/jakarta/data/Restriction.java @@ -17,68 +17,149 @@ */ package jakarta.data; +import jakarta.data.repository.Pattern; + import java.util.List; /** - * Represents a restriction condition for repository queries, defining an entity's attribute, - * an operator, and a comparison value for flexible query construction. + * Represents a condition used to filter values in repository queries. + * + *

The `Restriction` interface defines various types of conditions, including equality, + * comparison, range, null checks, and pattern matching checks, to support flexible and + * type-safe filtering.

* - *

Restrictions are used to specify filtering conditions, supporting type-safe attributes and - * a range of operators such as {@code EQUAL}, {@code LIKE}, {@code BETWEEN}, and more.

+ *

Static factory methods are provided to create instances of `Restriction` with + * specific conditions, using `BasicRestriction` as the underlying implementation.

* - *

Example usage:

*
- * Restriction titleRestriction = Restriction.like("title", "Data%");
- * Restriction dateRestriction = Restriction.between("publicationDate", pastDate, LocalDate.now());
+ * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
+ *
+ * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
+ *
+ * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
+ *
+ * Restriction authorIsNull = Restriction.isNull("author");
+ *
+ * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
+ *
+ * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
  * 
* - * @param the entity type that this restriction applies to, ensuring type-safe usage. + * @param the type of the entity on which the restriction is applied. */ -public record Restriction(String field, Operator operator, Object value) { +public interface Restriction { /** - * Constructs a restriction condition. + * The name of the field on which this restriction is applied. * - * @param field the name of the field to apply the restriction to (e.g., "title" or "publicationDate"). - * @param operator the operator defining the comparison or condition (e.g., `Operator.LIKE`). - * @param value the value to compare the field against. + * @return the field name. */ - public Restriction { - // Validation or processing logic can go here if needed - } + String field(); /** - * Creates a basic restriction condition. + * The operator for this restriction. * - * @param field the field name of the entity. - * @param operator the operator defining the restriction type. - * @param value the value to apply in the restriction. - * @return a restriction condition. + * @return the operator defining the restriction type. */ - public static Restriction where(String field, Operator operator, Object value) { - return new Restriction<>(field, operator, value); - } + Operator operator(); /** - * Creates a LIKE restriction with a pattern. + * The value used in this restriction, if applicable. * - * @param field the field name of the entity. - * @param pattern the pattern to match. - * @return a LIKE restriction condition. + * @return the comparison value, or null if the restriction does not use a value. + */ + Object value(); + + // Static Factory Methods + + /** + * Creates an equality restriction for the specified field and value. + *
+     * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
+     * 
*/ - public static Restriction like(String field, String pattern) { - return new Restriction<>(field, Operator.LIKE, pattern); + static Restriction equal(String field, Object value) { + return new BasicRestriction<>(field, Operator.EQUAL, value); } /** - * Creates a BETWEEN restriction for range queries. - * - * @param field the field name of the entity. - * @param start the starting value of the range. - * @param end the ending value of the range. - * @return a BETWEEN restriction condition. + * Creates a restriction for values greater than the specified value. + *
+     * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
+     * 
+ */ + static Restriction greaterThan(String field, Object value) { + return new BasicRestriction<>(field, Operator.GREATER_THAN, value); + } + + /** + * Creates a restriction for values greater than or equal to the specified value. + *
+     * Restriction ratingAtLeast = Restriction.greaterThanOrEqual("rating", 4.0);
+     * 
+ */ + static Restriction greaterThanOrEqual(String field, Object value) { + return new BasicRestriction<>(field, Operator.GREATER_THAN_EQUAL, value); + } + + /** + * Creates a restriction for values less than the specified value. + *
+     * Restriction ratingLessThan = Restriction.lessThan("rating", 3.0);
+     * 
+ */ + static Restriction lessThan(String field, Object value) { + return new BasicRestriction<>(field, Operator.LESS_THAN, value); + } + + /** + * Creates a restriction for values less than or equal to the specified value. + *
+     * Restriction ratingMax = Restriction.lessThanOrEqual("rating", 5.0);
+     * 
+ */ + static Restriction lessThanOrEqual(String field, Object value) { + return new BasicRestriction<>(field, Operator.LESS_THAN_EQUAL, value); + } + + /** + * Creates a restriction for a range of values between the specified start and end. + *
+     * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
+     * 
+ */ + static Restriction between(String field, Object start, Object end) { + return new BasicRestriction<>(field, Operator.BETWEEN, List.of(start, end)); + } + + /** + * Creates a restriction to check if the specified field is null. + *
+     * Restriction authorIsNull = Restriction.isNull("author");
+     * 
+ */ + static Restriction isNull(String field) { + return new BasicRestriction<>(field, Operator.IS_NULL, null); + } + + + /** + * Creates a `LIKE` restriction using a simple string pattern. + *
+     * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
+     * 
+ */ + static Restriction like(String field, String pattern) { + return new BasicRestriction<>(field, Operator.LIKE, pattern); + } + + /** + * Creates a `LIKE` restriction using a `Pattern`, supporting complex `LIKE` operations. + *
+     * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
+     * 
*/ - public static Restriction between(String field, Object start, Object end) { - return new Restriction<>(field, Operator.BETWEEN, List.of(start, end)); + static Restriction like(String field, Pattern pattern) { + return pattern; } } diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 2906554d6..5b2000b03 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -17,6 +17,7 @@ */ package jakarta.data.metamodel; +import jakarta.data.Restriction; import jakarta.data.Sort; /** @@ -32,4 +33,19 @@ public interface Attribute { * @return the entity attribute name. */ String name(); + + /** + * Creates an equality restriction for the attribute. + * + * @param value the value to match exactly. + * @return a Restriction representing an equality condition. + */ + Restriction equal(Object value); + + /** + * Creates a restriction for checking if the attribute is null. + * + * @return a Restriction representing the condition where the attribute is null. + */ + Restriction isNull(); } diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index 559960a41..f108b87fd 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -17,6 +17,7 @@ */ package jakarta.data.metamodel; +import jakarta.data.Restriction; import jakarta.data.Sort; /** @@ -49,4 +50,45 @@ public interface SortableAttribute extends Attribute { */ Sort desc(); + /** + * Creates a restriction for values greater than the specified value. + * + * @param value the lower bound (exclusive) for the attribute. + * @return a Restriction representing a greater-than condition. + */ + Restriction greaterThan(Object value); + + /** + * Creates a restriction for values greater than or equal to the specified value. + * + * @param value the lower bound (inclusive) for the attribute. + * @return a Restriction representing a greater-than-or-equal condition. + */ + Restriction greaterThanOrEqual(Object value); + + /** + * Creates a restriction for values less than the specified value. + * + * @param value the upper bound (exclusive) for the attribute. + * @return a Restriction representing a less-than condition. + */ + Restriction lessThan(Object value); + + /** + * Creates a restriction for values less than or equal to the specified value. + * + * @param value the upper bound (inclusive) for the attribute. + * @return a Restriction representing a less-than-or-equal condition. + */ + Restriction lessThanOrEqual(Object value); + + /** + * Creates a restriction that matches values within the specified range. + * + * @param start the starting value of the range (inclusive). + * @param end the ending value of the range (inclusive). + * @return a Restriction representing a range condition. + */ + Restriction between(Object start, Object end); + } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index c00390f09..55ed506b9 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -17,7 +17,10 @@ */ package jakarta.data.metamodel; +import jakarta.data.Restriction; import jakarta.data.Sort; +import jakarta.data.repository.Pattern; + /** * Represents an textual entity attribute in the {@link StaticMetamodel}. @@ -40,4 +43,15 @@ public interface TextAttribute extends SortableAttribute { */ Sort descIgnoreCase(); + /** + * Creates a `LIKE` restriction using a `Pattern` for the attribute, + * supporting different `LIKE` options such as prefix, suffix, and substring matching. + * + * @param pattern the pattern to match, defined using the `Pattern` class. + * @return a Restriction representing the `LIKE` condition. + */ + default Restriction like(Pattern pattern) { + return pattern; + } + } diff --git a/api/src/main/java/jakarta/data/repository/Pattern.java b/api/src/main/java/jakarta/data/repository/Pattern.java index 322daf50f..e61bfcbc7 100644 --- a/api/src/main/java/jakarta/data/repository/Pattern.java +++ b/api/src/main/java/jakarta/data/repository/Pattern.java @@ -4,30 +4,51 @@ import jakarta.data.Restriction; import java.util.function.Supplier; + + /** - * Represents a pattern for SQL `LIKE` operations, encapsulating different - * `LIKE` matching options, such as prefix, suffix, and substring matching, - * and supplying a `Restriction` for easy use in repository queries. + * Represents a pattern-based restriction for matching operations, encapsulating different + * options such as prefix, suffix, and substring matching. This implementation + * allows flexibility in creating pattern-based conditions directly as `Restriction` instances. + * + *

Example usage:

+ *
+ * // Case-sensitive exact match
+ * Restriction exactMatch = Pattern.like("title", "Jakarta");
  *
- * 

This class supports type-safe attributes and various matching types, - * including case-insensitive patterns.

+ * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase("title", "Jak"); * - * @param the type of the entity on which the restriction will be applied. + * Restriction suffixMatch = Pattern.suffixed("title", "Data"); + * + * Restriction substringIgnoreCase = Pattern.substringedIgnoreCase("title", "Java"); + *
+ * + * @param the type of the entity on which the restriction is applied. */ -public record Pattern(String field, String value, boolean ignoreCase) implements Supplier> { +public record Pattern(String field, String value, boolean ignoreCase) implements Restriction { + + /** + * The operator for pattern-based restrictions, fixed as `LIKE`. + * + * @return the `LIKE` operator. + */ + @Override + public Operator operator() { + return Operator.LIKE; + } /** - * Supplies a `Restriction` configured with this `LIKE` pattern. + * The value of the pattern used for matching. * - * @return a `Restriction` representing the `LIKE` pattern. + * @return the pattern value. */ @Override - public Restriction get() { - return new Restriction<>(field, Operator.LIKE, value); + public String value() { + return value; } /** - * Creates a case-sensitive `LIKE` pattern for an exact match. + * Creates a case-sensitive pattern match for an exact match. * * @param field the field to apply the pattern on. * @param value the value to match exactly. @@ -38,7 +59,7 @@ public static Pattern like(String field, String value) { } /** - * Creates a case-insensitive `LIKE` pattern for an exact match. + * Creates a case-insensitive pattern match for an exact match. * * @param field the field to apply the pattern on. * @param value the value to match exactly. @@ -49,7 +70,7 @@ public static Pattern likeIgnoreCase(String field, String value) { } /** - * Creates a `LIKE` pattern for values prefixed with the specified value. + * Creates a pattern match for values prefixed with the specified value. * * @param field the field to apply the pattern on. * @param value the prefix to match. @@ -60,7 +81,7 @@ public static Pattern prefixed(String field, String value) { } /** - * Creates a case-insensitive `LIKE` pattern for values prefixed with the specified value. + * Creates a case-insensitive pattern match for values prefixed with the specified value. * * @param field the field to apply the pattern on. * @param value the prefix to match. @@ -71,7 +92,7 @@ public static Pattern prefixedIgnoreCase(String field, String value) { } /** - * Creates a `LIKE` pattern for values containing the specified substring. + * Creates a pattern match for values containing the specified substring. * * @param field the field to apply the pattern on. * @param value the substring to match. @@ -82,7 +103,7 @@ public static Pattern substringed(String field, String value) { } /** - * Creates a case-insensitive `LIKE` pattern for values containing the specified substring. + * Creates a case-insensitive pattern match for values containing the specified substring. * * @param field the field to apply the pattern on. * @param value the substring to match. @@ -93,7 +114,7 @@ public static Pattern substringedIgnoreCase(String field, String value) { } /** - * Creates a `LIKE` pattern for values suffixed with the specified value. + * Creates a pattern match for values suffixed with the specified value. * * @param field the field to apply the pattern on. * @param value the suffix to match. @@ -104,7 +125,7 @@ public static Pattern suffixed(String field, String value) { } /** - * Creates a case-insensitive `LIKE` pattern for values suffixed with the specified value. + * Creates a case-insensitive pattern match for values suffixed with the specified value. * * @param field the field to apply the pattern on. * @param value the suffix to match. From 076062fc2f211c2652599534d4ed884e97f0c98f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 15:53:06 +0000 Subject: [PATCH 11/57] feat: update the implementation using record metamodel Signed-off-by: Otavio Santana --- .../data/metamodel/impl/AttributeRecord.java | 11 +++++ .../impl/SortableAttributeRecord.java | 36 ++++++++++++++++ .../metamodel/impl/TextAttributeRecord.java | 42 +++++++++++++++++++ .../java/jakarta/data/repository/Pattern.java | 18 +++++++- 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java index 31d329af7..c698da337 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java @@ -17,6 +17,7 @@ */ package jakarta.data.metamodel.impl; +import jakarta.data.Restriction; import jakarta.data.metamodel.Attribute; /** @@ -28,5 +29,15 @@ */ public record AttributeRecord(String name) implements Attribute { + + @Override + public Restriction equal(Object value) { + return Restriction.equal(name, value); + } + + @Override + public Restriction isNull() { + return Restriction.isNull(name); + } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java index 73f1df791..587ffa41d 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java @@ -17,6 +17,7 @@ */ package jakarta.data.metamodel.impl; +import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.SortableAttribute; @@ -37,5 +38,40 @@ public Sort asc() { public Sort desc() { return Sort.desc(name); } + + @Override + public Restriction equal(Object value) { + return Restriction.equal(name, value); + } + + @Override + public Restriction isNull() { + return Restriction.isNull(name); + } + + @Override + public Restriction greaterThan(Object value) { + return Restriction.greaterThan(name, value); + } + + @Override + public Restriction greaterThanOrEqual(Object value) { + return Restriction.greaterThanOrEqual(name, value); + } + + @Override + public Restriction lessThan(Object value) { + return Restriction.lessThan(name, value); + } + + @Override + public Restriction lessThanOrEqual(Object value) { + return Restriction.lessThanOrEqual(name, value); + } + + @Override + public Restriction between(Object start, Object end) { + return Restriction.between(name, start, end); + } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java index 4fe325751..3f452f23a 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java @@ -17,8 +17,10 @@ */ package jakarta.data.metamodel.impl; +import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.TextAttribute; +import jakarta.data.repository.Pattern; /** * Record type implementing {@link jakarta.data.metamodel.TextAttribute}. @@ -47,4 +49,44 @@ public Sort ascIgnoreCase() { public Sort descIgnoreCase() { return Sort.descIgnoreCase(name); } + + @Override + public Restriction equal(Object value) { + return Restriction.equal(name, value); + } + + @Override + public Restriction isNull() { + return Restriction.isNull(name); + } + + @Override + public Restriction greaterThan(Object value) { + return Restriction.greaterThan(name, value); + } + + @Override + public Restriction greaterThanOrEqual(Object value) { + return Restriction.greaterThanOrEqual(name, value); + } + + @Override + public Restriction lessThan(Object value) { + return Restriction.lessThan(name, value); + } + + @Override + public Restriction lessThanOrEqual(Object value) { + return Restriction.lessThanOrEqual(name, value); + } + + @Override + public Restriction between(Object start, Object end) { + return Restriction.between(name, start, end); + } + + @Override + public Restriction like(Pattern pattern) { + return pattern; + } } diff --git a/api/src/main/java/jakarta/data/repository/Pattern.java b/api/src/main/java/jakarta/data/repository/Pattern.java index e61bfcbc7..25866f3d8 100644 --- a/api/src/main/java/jakarta/data/repository/Pattern.java +++ b/api/src/main/java/jakarta/data/repository/Pattern.java @@ -1,9 +1,25 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package jakarta.data.repository; import jakarta.data.Operator; import jakarta.data.Restriction; -import java.util.function.Supplier; /** From 7697efdd38590345f5df9a660eecd0edf43e73e3 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 16:36:53 +0000 Subject: [PATCH 12/57] feat: move attribute to defult method Signed-off-by: Otavio Santana --- .../jakarta/data/metamodel/Attribute.java | 8 +- .../data/metamodel/SortableAttribute.java | 20 +++-- .../jakarta/data/metamodel/TextAttribute.java | 81 +++++++++++++++++-- .../data/metamodel/impl/AttributeRecord.java | 10 --- .../impl/SortableAttributeRecord.java | 34 -------- .../metamodel/impl/TextAttributeRecord.java | 39 --------- 6 files changed, 96 insertions(+), 96 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 5b2000b03..0ac9bfa64 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -40,12 +40,16 @@ public interface Attribute { * @param value the value to match exactly. * @return a Restriction representing an equality condition. */ - Restriction equal(Object value); + default Restriction equal(Object value) { + return Restriction.equal(name(), value); + } /** * Creates a restriction for checking if the attribute is null. * * @return a Restriction representing the condition where the attribute is null. */ - Restriction isNull(); + default Restriction isNull(){ + return Restriction.isNull(name()); + } } diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index f108b87fd..7cc2fb5a1 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -56,7 +56,9 @@ public interface SortableAttribute extends Attribute { * @param value the lower bound (exclusive) for the attribute. * @return a Restriction representing a greater-than condition. */ - Restriction greaterThan(Object value); + default Restriction greaterThan(Object value) { + return Restriction.greaterThan(name(), value); + } /** * Creates a restriction for values greater than or equal to the specified value. @@ -64,7 +66,9 @@ public interface SortableAttribute extends Attribute { * @param value the lower bound (inclusive) for the attribute. * @return a Restriction representing a greater-than-or-equal condition. */ - Restriction greaterThanOrEqual(Object value); + default Restriction greaterThanOrEqual(Object value) { + return Restriction.greaterThanOrEqual(name(), value); + } /** * Creates a restriction for values less than the specified value. @@ -72,7 +76,9 @@ public interface SortableAttribute extends Attribute { * @param value the upper bound (exclusive) for the attribute. * @return a Restriction representing a less-than condition. */ - Restriction lessThan(Object value); + default Restriction lessThan(Object value) { + return Restriction.lessThan(name(), value); + } /** * Creates a restriction for values less than or equal to the specified value. @@ -80,7 +86,9 @@ public interface SortableAttribute extends Attribute { * @param value the upper bound (inclusive) for the attribute. * @return a Restriction representing a less-than-or-equal condition. */ - Restriction lessThanOrEqual(Object value); + default Restriction lessThanOrEqual(Object value) { + return Restriction.lessThanOrEqual(name(), value); + } /** * Creates a restriction that matches values within the specified range. @@ -89,6 +97,8 @@ public interface SortableAttribute extends Attribute { * @param end the ending value of the range (inclusive). * @return a Restriction representing a range condition. */ - Restriction between(Object start, Object end); + default Restriction between(Object start, Object end) { + return Restriction.between(name(), start, end); + } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 55ed506b9..eae77de4c 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -44,14 +44,83 @@ public interface TextAttribute extends SortableAttribute { Sort descIgnoreCase(); /** - * Creates a `LIKE` restriction using a `Pattern` for the attribute, - * supporting different `LIKE` options such as prefix, suffix, and substring matching. + * Creates a `LIKE` restriction for an exact match on the specified text. * - * @param pattern the pattern to match, defined using the `Pattern` class. - * @return a Restriction representing the `LIKE` condition. + * @param text the text to match exactly. + * @return a Restriction representing a case-sensitive `LIKE` condition. */ - default Restriction like(Pattern pattern) { - return pattern; + default Restriction like(String text) { + return Pattern.like(name(), text); + } + + /** + * Creates a case-insensitive `LIKE` restriction for an exact match on the specified text. + * + * @param text the text to match exactly. + * @return a Restriction representing a case-insensitive `LIKE` condition. + */ + default Restriction likeIgnoreCase(String text) { + return Pattern.likeIgnoreCase(name(), text); + } + + /** + * Creates a `LIKE` restriction for values that start with the specified text. + * + * @param text the text prefix to match. + * @return a Restriction representing a prefix `LIKE` condition. + */ + default Restriction startsWith(String text) { + return Pattern.prefixed(name(), text); + } + + /** + * Creates a case-insensitive `LIKE` restriction for values that start with the specified text. + * + * @param text the text prefix to match. + * @return a Restriction representing a case-insensitive prefix `LIKE` condition. + */ + default Restriction startsWithIgnoreCase(String text) { + return Pattern.prefixedIgnoreCase(name(), text); + } + + /** + * Creates a `LIKE` restriction for values that contain the specified substring. + * + * @param text the substring to match. + * @return a Restriction representing a substring `LIKE` condition. + */ + default Restriction contains(String text) { + return Pattern.substringed(name(), text); + } + + /** + * Creates a case-insensitive `LIKE` restriction for values that contain the specified substring. + * + * @param text the substring to match. + * @return a Restriction representing a case-insensitive substring `LIKE` condition. + */ + default Restriction containsIgnoreCase(String text) { + return Pattern.substringedIgnoreCase(name(), text); + } + + /** + * Creates a `LIKE` restriction for values that end with the specified text. + * + * @param text the text suffix to match. + * @return a Restriction representing a suffix `LIKE` condition. + */ + default Restriction endsWith(String text) { + return Pattern.suffixed(name(), text); + } + + /** + * Creates a case-insensitive `LIKE` restriction for values that end with the specified text. + * + * @param text the text suffix to match. + * @return a Restriction representing a case-insensitive suffix `LIKE` condition. + */ + default Restriction endsWithIgnoreCase(String text) { + return Pattern.suffixedIgnoreCase(name(), text); } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java index c698da337..48fc78bde 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/AttributeRecord.java @@ -17,7 +17,6 @@ */ package jakarta.data.metamodel.impl; -import jakarta.data.Restriction; import jakarta.data.metamodel.Attribute; /** @@ -30,14 +29,5 @@ public record AttributeRecord(String name) implements Attribute { - @Override - public Restriction equal(Object value) { - return Restriction.equal(name, value); - } - - @Override - public Restriction isNull() { - return Restriction.isNull(name); - } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java index 587ffa41d..08d25abcc 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java @@ -39,39 +39,5 @@ public Sort desc() { return Sort.desc(name); } - @Override - public Restriction equal(Object value) { - return Restriction.equal(name, value); - } - - @Override - public Restriction isNull() { - return Restriction.isNull(name); - } - - @Override - public Restriction greaterThan(Object value) { - return Restriction.greaterThan(name, value); - } - - @Override - public Restriction greaterThanOrEqual(Object value) { - return Restriction.greaterThanOrEqual(name, value); - } - - @Override - public Restriction lessThan(Object value) { - return Restriction.lessThan(name, value); - } - - @Override - public Restriction lessThanOrEqual(Object value) { - return Restriction.lessThanOrEqual(name, value); - } - - @Override - public Restriction between(Object start, Object end) { - return Restriction.between(name, start, end); - } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java index 3f452f23a..f35da41bc 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java @@ -50,43 +50,4 @@ public Sort descIgnoreCase() { return Sort.descIgnoreCase(name); } - @Override - public Restriction equal(Object value) { - return Restriction.equal(name, value); - } - - @Override - public Restriction isNull() { - return Restriction.isNull(name); - } - - @Override - public Restriction greaterThan(Object value) { - return Restriction.greaterThan(name, value); - } - - @Override - public Restriction greaterThanOrEqual(Object value) { - return Restriction.greaterThanOrEqual(name, value); - } - - @Override - public Restriction lessThan(Object value) { - return Restriction.lessThan(name, value); - } - - @Override - public Restriction lessThanOrEqual(Object value) { - return Restriction.lessThanOrEqual(name, value); - } - - @Override - public Restriction between(Object start, Object end) { - return Restriction.between(name, start, end); - } - - @Override - public Restriction like(Pattern pattern) { - return pattern; - } } From 4be722f264fd04da8b5662441813202b9687c83f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 16:38:10 +0000 Subject: [PATCH 13/57] style: remove unsed imports Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Order.java | 8 ++++---- .../main/java/jakarta/data/metamodel/StaticMetamodel.java | 4 ++-- .../data/metamodel/impl/SortableAttributeRecord.java | 1 - .../jakarta/data/metamodel/impl/TextAttributeRecord.java | 2 -- api/src/main/java/jakarta/data/page/CursoredPage.java | 5 +++-- api/src/main/java/jakarta/data/repository/OrderBy.java | 1 + 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/jakarta/data/Order.java b/api/src/main/java/jakarta/data/Order.java index 3cc99a502..02b7d4a69 100644 --- a/api/src/main/java/jakarta/data/Order.java +++ b/api/src/main/java/jakarta/data/Order.java @@ -17,12 +17,12 @@ */ package jakarta.data; -import java.util.Iterator; -import java.util.List; - import jakarta.data.metamodel.StaticMetamodel; import jakarta.data.repository.OrderBy; +import java.util.Iterator; +import java.util.List; + /** *

Requests sorting on various entity attributes.

* @@ -169,4 +169,4 @@ public Iterator> iterator() { public String toString() { return sorts.toString(); } -} \ No newline at end of file +} diff --git a/api/src/main/java/jakarta/data/metamodel/StaticMetamodel.java b/api/src/main/java/jakarta/data/metamodel/StaticMetamodel.java index b0a4f47a5..1e644833f 100644 --- a/api/src/main/java/jakarta/data/metamodel/StaticMetamodel.java +++ b/api/src/main/java/jakarta/data/metamodel/StaticMetamodel.java @@ -17,14 +17,14 @@ */ package jakarta.data.metamodel; +import jakarta.data.Sort; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import jakarta.data.Sort; - /** *

Annotates a class which serves as a static metamodel for an entity, enabling * type-safe access to entity attribute names and related objects such as instances diff --git a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java index 08d25abcc..ed8c15211 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/SortableAttributeRecord.java @@ -17,7 +17,6 @@ */ package jakarta.data.metamodel.impl; -import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.SortableAttribute; diff --git a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java index f35da41bc..720df286d 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/TextAttributeRecord.java @@ -17,10 +17,8 @@ */ package jakarta.data.metamodel.impl; -import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.TextAttribute; -import jakarta.data.repository.Pattern; /** * Record type implementing {@link jakarta.data.metamodel.TextAttribute}. diff --git a/api/src/main/java/jakarta/data/page/CursoredPage.java b/api/src/main/java/jakarta/data/page/CursoredPage.java index 272409bb8..d8b7a51c0 100644 --- a/api/src/main/java/jakarta/data/page/CursoredPage.java +++ b/api/src/main/java/jakarta/data/page/CursoredPage.java @@ -17,9 +17,10 @@ */ package jakarta.data.page; -import jakarta.data.repository.OrderBy; import jakarta.data.Order; import jakarta.data.Sort; +import jakarta.data.repository.OrderBy; + import java.util.NoSuchElementException; /** @@ -208,4 +209,4 @@ public interface CursoredPage extends Page { */ @Override PageRequest previousPageRequest(); -} \ No newline at end of file +} diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java index cd8db4988..9b2044799 100644 --- a/api/src/main/java/jakarta/data/repository/OrderBy.java +++ b/api/src/main/java/jakarta/data/repository/OrderBy.java @@ -19,6 +19,7 @@ import jakarta.data.Order; import jakarta.data.Sort; + import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; From 08de315200fca6117028f37509326c531bb9ac48 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 16:41:54 +0000 Subject: [PATCH 14/57] feat: Use the CompositeRestriction Signed-off-by: Otavio Santana --- .../main/java/jakarta/data/CompositeRestriction.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/jakarta/data/CompositeRestriction.java b/api/src/main/java/jakarta/data/CompositeRestriction.java index 873f6a68e..4f30365b1 100644 --- a/api/src/main/java/jakarta/data/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/CompositeRestriction.java @@ -17,6 +17,7 @@ */ package jakarta.data; +import java.util.Iterator; import java.util.List; /** @@ -28,7 +29,7 @@ * * @param the entity type that the restrictions apply to. */ -public record CompositeRestriction(LogicalOperator operator, List> restrictions) { +public record CompositeRestriction(LogicalOperator operator, List> restrictions) implements Iterable> { /** * Constructs a composite restriction with the specified operator and list of restrictions. @@ -40,7 +41,10 @@ public record CompositeRestriction(LogicalOperator operator, List> iterator() { + return restrictions.iterator(); + } /** * Creates a composite restriction where all specified restrictions must be true (AND logic). @@ -65,4 +69,6 @@ public static CompositeRestriction all(Restriction... restrictions) { public static CompositeRestriction any(Restriction... restrictions) { return new CompositeRestriction<>(LogicalOperator.OR, List.of(restrictions)); } + + } From 391ab6f7be1d0967c1a7bb641bd63cf909870eee Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:33:05 +0000 Subject: [PATCH 15/57] feat: remove filterable attribute Signed-off-by: Otavio Santana --- .../data/metamodel/FilterableAttribute.java | 102 ------------------ 1 file changed, 102 deletions(-) delete mode 100644 api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java diff --git a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java b/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java deleted file mode 100644 index cf214568e..000000000 --- a/api/src/main/java/jakarta/data/metamodel/FilterableAttribute.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data.metamodel; - -import jakarta.data.Restriction; - -import java.util.function.Supplier; - -/** - * Represents an entity attribute that supports filtering operations in repository queries, - * including type-safe comparisons, range restrictions, and pattern matching (e.g., LIKE queries). - * - *

The `FilterableAttribute` interface provides methods for creating various filtering - * restrictions, enabling expressive and type-safe query construction for attributes - * such as numeric values, dates, and strings.

- * - *

Example usage:

- *
- * // Define filtering conditions on entity attributes
- * Restriction titleRestriction = _Book.title.like(Pattern.prefixedIgnoreCase("Jak"));
- * Restriction dateRestriction = _Book.publicationDate.between(pastDate, LocalDate.now());
- * 
- * - * @param the entity type that this attribute belongs to. - */ -public interface FilterableAttribute { - - /** - * Creates an equality restriction for the attribute. - * - * @param value the value to match exactly. - * @return a Restriction representing an equality condition. - */ - Restriction equal(Object value); - - /** - * Creates a restriction for values greater than the specified value. - * - * @param value the lower bound (exclusive) for the attribute. - * @return a Restriction representing a greater-than condition. - */ - Restriction greaterThan(Object value); - - /** - * Creates a restriction for values greater than or equal to the specified value. - * - * @param value the lower bound (inclusive) for the attribute. - * @return a Restriction representing a greater-than-or-equal condition. - */ - Restriction greaterThanOrEqual(Object value); - - /** - * Creates a restriction for values less than the specified value. - * - * @param value the upper bound (exclusive) for the attribute. - * @return a Restriction representing a less-than condition. - */ - Restriction lessThan(Object value); - - /** - * Creates a restriction for values less than or equal to the specified value. - * - * @param value the upper bound (inclusive) for the attribute. - * @return a Restriction representing a less-than-or-equal condition. - */ - Restriction lessThanOrEqual(Object value); - - /** - * Creates a restriction that matches values within the specified range. - * - * @param start the starting value of the range (inclusive). - * @param end the ending value of the range (inclusive). - * @return a Restriction representing a range condition. - */ - Restriction between(Object start, Object end); - - /** - * Creates a `LIKE` restriction using a `Pattern` for the attribute, - * supporting different `LIKE` options such as prefix, suffix, and substring matching. - * - * @param pattern the pattern to match, defined using the `Pattern` class. - * @return a Restriction representing the `LIKE` condition. - */ - default Restriction like(Supplier> pattern) { - return pattern.get(); - } -} From 0a2a63a87536e7e7832e1fff2a1af6b903c49fa1 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:40:46 +0000 Subject: [PATCH 16/57] feat: move restriction to metadmodel Signed-off-by: Otavio Santana --- .../java/jakarta/data/LogicalOperator.java | 2 + .../main/java/jakarta/data/Restriction.java | 165 ------------------ .../jakarta/data/metamodel/Attribute.java | 6 +- .../{ => metamodel}/BasicRestriction.java | 4 +- .../{ => metamodel}/CompositeRestriction.java | 4 +- .../jakarta/data/metamodel/Restriction.java | 70 ++++++++ .../data/metamodel/SortableAttribute.java | 14 +- .../jakarta/data/metamodel/TextAttribute.java | 1 - .../java/jakarta/data/repository/Pattern.java | 2 +- 9 files changed, 90 insertions(+), 178 deletions(-) delete mode 100644 api/src/main/java/jakarta/data/Restriction.java rename api/src/main/java/jakarta/data/{ => metamodel}/BasicRestriction.java (96%) rename api/src/main/java/jakarta/data/{ => metamodel}/CompositeRestriction.java (97%) create mode 100644 api/src/main/java/jakarta/data/metamodel/Restriction.java diff --git a/api/src/main/java/jakarta/data/LogicalOperator.java b/api/src/main/java/jakarta/data/LogicalOperator.java index 0e82c8035..f512f16a4 100644 --- a/api/src/main/java/jakarta/data/LogicalOperator.java +++ b/api/src/main/java/jakarta/data/LogicalOperator.java @@ -17,6 +17,8 @@ */ package jakarta.data; +import jakarta.data.metamodel.Restriction; + /** * Enum representing logical operators for combining multiple {@link Restriction} objects. */ diff --git a/api/src/main/java/jakarta/data/Restriction.java b/api/src/main/java/jakarta/data/Restriction.java deleted file mode 100644 index 3be8b995b..000000000 --- a/api/src/main/java/jakarta/data/Restriction.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data; - -import jakarta.data.repository.Pattern; - -import java.util.List; - -/** - * Represents a condition used to filter values in repository queries. - * - *

The `Restriction` interface defines various types of conditions, including equality, - * comparison, range, null checks, and pattern matching checks, to support flexible and - * type-safe filtering.

- * - *

Static factory methods are provided to create instances of `Restriction` with - * specific conditions, using `BasicRestriction` as the underlying implementation.

- * - *
- * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
- *
- * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
- *
- * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
- *
- * Restriction authorIsNull = Restriction.isNull("author");
- *
- * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
- *
- * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
- * 
- * - * @param the type of the entity on which the restriction is applied. - */ -public interface Restriction { - - /** - * The name of the field on which this restriction is applied. - * - * @return the field name. - */ - String field(); - - /** - * The operator for this restriction. - * - * @return the operator defining the restriction type. - */ - Operator operator(); - - /** - * The value used in this restriction, if applicable. - * - * @return the comparison value, or null if the restriction does not use a value. - */ - Object value(); - - // Static Factory Methods - - /** - * Creates an equality restriction for the specified field and value. - *
-     * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
-     * 
- */ - static Restriction equal(String field, Object value) { - return new BasicRestriction<>(field, Operator.EQUAL, value); - } - - /** - * Creates a restriction for values greater than the specified value. - *
-     * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
-     * 
- */ - static Restriction greaterThan(String field, Object value) { - return new BasicRestriction<>(field, Operator.GREATER_THAN, value); - } - - /** - * Creates a restriction for values greater than or equal to the specified value. - *
-     * Restriction ratingAtLeast = Restriction.greaterThanOrEqual("rating", 4.0);
-     * 
- */ - static Restriction greaterThanOrEqual(String field, Object value) { - return new BasicRestriction<>(field, Operator.GREATER_THAN_EQUAL, value); - } - - /** - * Creates a restriction for values less than the specified value. - *
-     * Restriction ratingLessThan = Restriction.lessThan("rating", 3.0);
-     * 
- */ - static Restriction lessThan(String field, Object value) { - return new BasicRestriction<>(field, Operator.LESS_THAN, value); - } - - /** - * Creates a restriction for values less than or equal to the specified value. - *
-     * Restriction ratingMax = Restriction.lessThanOrEqual("rating", 5.0);
-     * 
- */ - static Restriction lessThanOrEqual(String field, Object value) { - return new BasicRestriction<>(field, Operator.LESS_THAN_EQUAL, value); - } - - /** - * Creates a restriction for a range of values between the specified start and end. - *
-     * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
-     * 
- */ - static Restriction between(String field, Object start, Object end) { - return new BasicRestriction<>(field, Operator.BETWEEN, List.of(start, end)); - } - - /** - * Creates a restriction to check if the specified field is null. - *
-     * Restriction authorIsNull = Restriction.isNull("author");
-     * 
- */ - static Restriction isNull(String field) { - return new BasicRestriction<>(field, Operator.IS_NULL, null); - } - - - /** - * Creates a `LIKE` restriction using a simple string pattern. - *
-     * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
-     * 
- */ - static Restriction like(String field, String pattern) { - return new BasicRestriction<>(field, Operator.LIKE, pattern); - } - - /** - * Creates a `LIKE` restriction using a `Pattern`, supporting complex `LIKE` operations. - *
-     * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
-     * 
- */ - static Restriction like(String field, Pattern pattern) { - return pattern; - } -} diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 0ac9bfa64..2fb85f439 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -17,7 +17,7 @@ */ package jakarta.data.metamodel; -import jakarta.data.Restriction; +import jakarta.data.Operator; import jakarta.data.Sort; /** @@ -41,7 +41,7 @@ public interface Attribute { * @return a Restriction representing an equality condition. */ default Restriction equal(Object value) { - return Restriction.equal(name(), value); + return new BasicRestriction<>(name(), Operator.EQUAL, value); } /** @@ -50,6 +50,6 @@ default Restriction equal(Object value) { * @return a Restriction representing the condition where the attribute is null. */ default Restriction isNull(){ - return Restriction.isNull(name()); + return new BasicRestriction<>(name(), Operator.IS_NULL, null); } } diff --git a/api/src/main/java/jakarta/data/BasicRestriction.java b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java similarity index 96% rename from api/src/main/java/jakarta/data/BasicRestriction.java rename to api/src/main/java/jakarta/data/metamodel/BasicRestriction.java index ac760b7c5..750f6a425 100644 --- a/api/src/main/java/jakarta/data/BasicRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java @@ -15,7 +15,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data; +package jakarta.data.metamodel; + +import jakarta.data.Operator; /** * A basic implementation of the `Restriction` interface, representing various conditions diff --git a/api/src/main/java/jakarta/data/CompositeRestriction.java b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java similarity index 97% rename from api/src/main/java/jakarta/data/CompositeRestriction.java rename to api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java index 4f30365b1..db944e244 100644 --- a/api/src/main/java/jakarta/data/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java @@ -15,7 +15,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data; +package jakarta.data.metamodel; + +import jakarta.data.LogicalOperator; import java.util.Iterator; import java.util.List; diff --git a/api/src/main/java/jakarta/data/metamodel/Restriction.java b/api/src/main/java/jakarta/data/metamodel/Restriction.java new file mode 100644 index 000000000..16dfc23cc --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/Restriction.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import jakarta.data.Operator; + +/** + * Represents a condition used to filter values in repository queries. + * + *

The `Restriction` interface defines various types of conditions, including equality, + * comparison, range, null checks, and pattern matching checks, to support flexible and + * type-safe filtering.

+ * + *

Static factory methods are provided to create instances of `Restriction` with + * specific conditions, using `BasicRestriction` as the underlying implementation.

+ * + *
+ * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
+ *
+ * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
+ *
+ * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
+ *
+ * Restriction authorIsNull = Restriction.isNull("author");
+ *
+ * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
+ *
+ * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
+ * 
+ * + * @param the type of the entity on which the restriction is applied. + */ +public interface Restriction { + + /** + * The name of the field on which this restriction is applied. + * + * @return the field name. + */ + String field(); + + /** + * The operator for this restriction. + * + * @return the operator defining the restriction type. + */ + Operator operator(); + + /** + * The value used in this restriction, if applicable. + * + * @return the comparison value, or null if the restriction does not use a value. + */ + Object value(); +} diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index 7cc2fb5a1..d4e42eb07 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -17,9 +17,11 @@ */ package jakarta.data.metamodel; -import jakarta.data.Restriction; +import jakarta.data.Operator; import jakarta.data.Sort; +import java.util.List; + /** * Represents a sortable entity attribute in the {@link StaticMetamodel}. * Entity attribute types that are sortable include: @@ -57,7 +59,7 @@ public interface SortableAttribute extends Attribute { * @return a Restriction representing a greater-than condition. */ default Restriction greaterThan(Object value) { - return Restriction.greaterThan(name(), value); + return new BasicRestriction<>(name(), Operator.GREATER_THAN, value); } /** @@ -67,7 +69,7 @@ default Restriction greaterThan(Object value) { * @return a Restriction representing a greater-than-or-equal condition. */ default Restriction greaterThanOrEqual(Object value) { - return Restriction.greaterThanOrEqual(name(), value); + return new BasicRestriction<>(name(), Operator.GREATER_THAN_EQUAL, value); } /** @@ -77,7 +79,7 @@ default Restriction greaterThanOrEqual(Object value) { * @return a Restriction representing a less-than condition. */ default Restriction lessThan(Object value) { - return Restriction.lessThan(name(), value); + return new BasicRestriction<>(name(), Operator.LESS_THAN, value); } /** @@ -87,7 +89,7 @@ default Restriction lessThan(Object value) { * @return a Restriction representing a less-than-or-equal condition. */ default Restriction lessThanOrEqual(Object value) { - return Restriction.lessThanOrEqual(name(), value); + return new BasicRestriction<>(name(), Operator.LESS_THAN_EQUAL, value); } /** @@ -98,7 +100,7 @@ default Restriction lessThanOrEqual(Object value) { * @return a Restriction representing a range condition. */ default Restriction between(Object start, Object end) { - return Restriction.between(name(), start, end); + return new BasicRestriction<>(name(), Operator.BETWEEN, List.of(start, end)); } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index eae77de4c..88b8289e7 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -17,7 +17,6 @@ */ package jakarta.data.metamodel; -import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.repository.Pattern; diff --git a/api/src/main/java/jakarta/data/repository/Pattern.java b/api/src/main/java/jakarta/data/repository/Pattern.java index 25866f3d8..d781f3151 100644 --- a/api/src/main/java/jakarta/data/repository/Pattern.java +++ b/api/src/main/java/jakarta/data/repository/Pattern.java @@ -18,7 +18,7 @@ package jakarta.data.repository; import jakarta.data.Operator; -import jakarta.data.Restriction; +import jakarta.data.metamodel.Restriction; From d4bc32cd4f6daac0470024d0d35e6a8bd6fd8619 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:42:53 +0000 Subject: [PATCH 17/57] feat: move ppattern to use internally Signed-off-by: Otavio Santana --- .../jakarta/data/{repository => metamodel}/Pattern.java | 6 ++---- api/src/main/java/jakarta/data/metamodel/TextAttribute.java | 1 - api/src/main/java/module-info.java | 3 ++- 3 files changed, 4 insertions(+), 6 deletions(-) rename api/src/main/java/jakarta/data/{repository => metamodel}/Pattern.java (96%) diff --git a/api/src/main/java/jakarta/data/repository/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java similarity index 96% rename from api/src/main/java/jakarta/data/repository/Pattern.java rename to api/src/main/java/jakarta/data/metamodel/Pattern.java index d781f3151..61096da4c 100644 --- a/api/src/main/java/jakarta/data/repository/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -15,11 +15,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.repository; +package jakarta.data.metamodel; import jakarta.data.Operator; -import jakarta.data.metamodel.Restriction; - /** @@ -41,7 +39,7 @@ * * @param the type of the entity on which the restriction is applied. */ -public record Pattern(String field, String value, boolean ignoreCase) implements Restriction { +record Pattern(String field, String value, boolean ignoreCase) implements Restriction { /** * The operator for pattern-based restrictions, fixed as `LIKE`. diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 88b8289e7..5f5258e31 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -18,7 +18,6 @@ package jakarta.data.metamodel; import jakarta.data.Sort; -import jakarta.data.repository.Pattern; /** diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index f52e8862f..e6980ccf2 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -1075,4 +1075,5 @@ exports jakarta.data.exceptions; opens jakarta.data.repository; exports jakarta.data.spi; -} \ No newline at end of file + opens jakarta.data.metamodel; +} From 596f49b7467c14596a665371684ebeff5f0cc6a4 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:45:52 +0000 Subject: [PATCH 18/57] chore: remove ISNULL condition Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Operator.java | 11 +---------- .../main/java/jakarta/data/metamodel/Attribute.java | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/jakarta/data/Operator.java b/api/src/main/java/jakarta/data/Operator.java index 748cb0451..8a184ba8d 100644 --- a/api/src/main/java/jakarta/data/Operator.java +++ b/api/src/main/java/jakarta/data/Operator.java @@ -72,16 +72,7 @@ public enum Operator { * Matches records where the field value falls within a specified range. * Typically used for range-based comparisons, such as finding dates between a start and end date. */ - BETWEEN, - - /** - * Matches records where the field value is null. - * - *

This operator is used to filter records where the specified field - * does not contain a value. Commonly used to find records with unset or - * missing data in a particular field.

- */ - IS_NULL + BETWEEN } diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 2fb85f439..b54ab8ad4 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -50,6 +50,6 @@ default Restriction equal(Object value) { * @return a Restriction representing the condition where the attribute is null. */ default Restriction isNull(){ - return new BasicRestriction<>(name(), Operator.IS_NULL, null); + return new BasicRestriction<>(name(), Operator.EQUAL, null); } } From 22b0f4af677178a8f9cbb9e17a779e531be76d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Wed, 6 Nov 2024 20:46:51 +0000 Subject: [PATCH 19/57] Update api/src/main/java/jakarta/data/metamodel/TextAttribute.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/TextAttribute.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 5f5258e31..562140216 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -42,10 +42,10 @@ public interface TextAttribute extends SortableAttribute { Sort descIgnoreCase(); /** - * Creates a `LIKE` restriction for an exact match on the specified text. + * Creates a `LIKE` restriction for a match on the specified text. * - * @param text the text to match exactly. - * @return a Restriction representing a case-sensitive `LIKE` condition. + * @param text the text to match. + * @return a Restriction representing a `LIKE` condition. */ default Restriction like(String text) { return Pattern.like(name(), text); From 34c00e5d77fd82c4aef7cfdea9436ddac083fb0d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:56:28 +0000 Subject: [PATCH 20/57] feat: create range class Signed-off-by: Otavio Santana --- .../java/jakarta/data/metamodel/Pattern.java | 2 +- .../java/jakarta/data/metamodel/Range.java | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/jakarta/data/metamodel/Range.java diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 61096da4c..19ec3f804 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -39,7 +39,7 @@ * * @param the type of the entity on which the restriction is applied. */ -record Pattern(String field, String value, boolean ignoreCase) implements Restriction { +public record Pattern(String field, String value, boolean ignoreCase) implements Restriction { /** * The operator for pattern-based restrictions, fixed as `LIKE`. diff --git a/api/src/main/java/jakarta/data/metamodel/Range.java b/api/src/main/java/jakarta/data/metamodel/Range.java new file mode 100644 index 000000000..65b8bdc4e --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/Range.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import jakarta.data.Operator; + +import java.util.List; + +/** + * Represents a range-based restriction for repository queries, allowing comparisons within + * a specified range or boundary on a particular entity attribute. + * + *

This class implements {@link Restriction} and provides flexible factory methods + * to create range-based conditions, supporting inclusive and exclusive bounds.

+ * + *

Usage examples:

+ *
+ * // A range between two dates (inclusive)
+ * Restriction publicationDateRange = Range.between(_Book.publicationDate, pastDate, LocalDate.now());
+ *
+ * // An exclusive range above a certain rating
+ * Restriction ratingAbove = Range.above(_Book.rating, 4.5);
+ *
+ * // An inclusive range up to a certain price
+ * Restriction priceTo = Range.to(_Book.price, 100);
+ * 
+ * + *

The `operator()` method dynamically determines the appropriate operator based on the range type:

+ *
    + *
  • If both bounds are specified, {@code Operator.BETWEEN} is used.
  • + *
  • If only the lower bound is specified and the range is exclusive, {@code Operator.GREATER_THAN} is used.
  • + *
  • If only the lower bound is specified and the range is inclusive, {@code Operator.GREATER_THAN_EQUAL} is used.
  • + *
  • If only the upper bound is specified and the range is exclusive, {@code Operator.LESS_THAN} is used.
  • + *
  • If only the upper bound is specified and the range is inclusive, {@code Operator.LESS_THAN_EQUAL} is used.
  • + *
+ * + * @param the type of the entity's attribute on which the range is applied. + */ +public record Range(String field, T lowerBound, T upperBound, boolean open) implements Restriction { + + @Override + public String field() { + return field; + } + + @Override + public Operator operator() { + if (lowerBound != null && upperBound != null) { + return Operator.BETWEEN; + } else if (lowerBound != null && open) { + return Operator.GREATER_THAN; + } else if (lowerBound != null) { + return Operator.GREATER_THAN_EQUAL; + } else if (upperBound != null && open) { + return Operator.LESS_THAN; + } else { + return Operator.LESS_THAN_EQUAL; + } + } + + @Override + public Object value() { + if (lowerBound != null && upperBound != null) { + return List.of(lowerBound, upperBound); + } + return lowerBound != null ? lowerBound : upperBound; + } + + public static Range between(String field, T lowerBound, T upperBound) { + return new Range<>(field, lowerBound, upperBound, false); + } + + public static Range from(String field, T lowerBound) { + return new Range<>(field, lowerBound, null, false); + } + + public static Range to(String field, T upperBound) { + return new Range<>(field, null, upperBound, false); + } + + public static Range above(String field, T lowerBound) { + return new Range<>(field, lowerBound, null, true); + } + + public static Range below(String field, T upperBound) { + return new Range<>(field, null, upperBound, true); + } +} From 785abd69e2c902e7ab57f0e3366af04ccfe9d135 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 20:56:51 +0000 Subject: [PATCH 21/57] docs: update documentation on pattern Signed-off-by: Otavio Santana --- .../main/java/jakarta/data/metamodel/Pattern.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 19ec3f804..42b757b43 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -25,16 +25,19 @@ * options such as prefix, suffix, and substring matching. This implementation * allows flexibility in creating pattern-based conditions directly as `Restriction` instances. * - *

Example usage:

+ *

Example usage with metadata attributes:

*
  * // Case-sensitive exact match
- * Restriction exactMatch = Pattern.like("title", "Jakarta");
+ * Restriction exactMatch = Pattern.like(_Book.title, "Jakarta Data");
  *
- * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase("title", "Jak");
+ * // Case-insensitive prefix match
+ * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase(_Book.title, "Jak");
  *
- * Restriction suffixMatch = Pattern.suffixed("title", "Data");
+ * // Case-sensitive suffix match
+ * Restriction suffixMatch = Pattern.suffixed(_Book.title, "Guide");
  *
- * Restriction substringIgnoreCase = Pattern.substringedIgnoreCase("title", "Java");
+ * // Case-insensitive substring match
+ * Restriction substringIgnoreCase = Pattern.substringedIgnoreCase(_Book.title, "Java");
  * 
* * @param the type of the entity on which the restriction is applied. From d51a97e1f7fe16db7e91b1e072d4a6aeca7b0468 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 21:05:39 +0000 Subject: [PATCH 22/57] feat: udpate using range Signed-off-by: Otavio Santana --- .../data/metamodel/SortableAttribute.java | 50 +++++++++++++++++-- .../jakarta/data/metamodel/TextAttribute.java | 16 +++--- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index d4e42eb07..8c5397a38 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -93,14 +93,54 @@ default Restriction lessThanOrEqual(Object value) { } /** - * Creates a restriction that matches values within the specified range. + * Creates a restriction for values within the specified range, inclusive of both bounds. * - * @param start the starting value of the range (inclusive). - * @param end the ending value of the range (inclusive). + * @param lowerBound the lower bound (inclusive). + * @param upperBound the upper bound (inclusive). * @return a Restriction representing a range condition. */ - default Restriction between(Object start, Object end) { - return new BasicRestriction<>(name(), Operator.BETWEEN, List.of(start, end)); + default Range between(T lowerBound, T upperBound) { + return Range.between(name(), lowerBound, upperBound); + } + + /** + * Creates a restriction for values greater than or equal to the specified lower bound. + * + * @param lowerBound the lower bound (inclusive). + * @return a Restriction representing a "greater than or equal" condition. + */ + default Range from(T lowerBound) { + return Range.from(name(), lowerBound); + } + + /** + * Creates a restriction for values less than or equal to the specified upper bound. + * + * @param upperBound the upper bound (inclusive). + * @return a Restriction representing a "less than or equal" condition. + */ + default Range to(T upperBound) { + return Range.to(name(), upperBound); + } + + /** + * Creates a restriction for values greater than the specified lower bound (exclusive). + * + * @param lowerBound the lower bound (exclusive). + * @return a Restriction representing a "greater than" condition. + */ + default Range above(T lowerBound) { + return Range.above(name(), lowerBound); + } + + /** + * Creates a restriction for values less than the specified upper bound (exclusive). + * + * @param upperBound the upper bound (exclusive). + * @return a Restriction representing a "less than" condition. + */ + default Range below(T upperBound) { + return Range.below(name(), upperBound); } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 562140216..330415721 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -47,7 +47,7 @@ public interface TextAttribute extends SortableAttribute { * @param text the text to match. * @return a Restriction representing a `LIKE` condition. */ - default Restriction like(String text) { + default Pattern like(String text) { return Pattern.like(name(), text); } @@ -57,7 +57,7 @@ default Restriction like(String text) { * @param text the text to match exactly. * @return a Restriction representing a case-insensitive `LIKE` condition. */ - default Restriction likeIgnoreCase(String text) { + default Pattern likeIgnoreCase(String text) { return Pattern.likeIgnoreCase(name(), text); } @@ -67,7 +67,7 @@ default Restriction likeIgnoreCase(String text) { * @param text the text prefix to match. * @return a Restriction representing a prefix `LIKE` condition. */ - default Restriction startsWith(String text) { + default Pattern startsWith(String text) { return Pattern.prefixed(name(), text); } @@ -77,7 +77,7 @@ default Restriction startsWith(String text) { * @param text the text prefix to match. * @return a Restriction representing a case-insensitive prefix `LIKE` condition. */ - default Restriction startsWithIgnoreCase(String text) { + default Pattern startsWithIgnoreCase(String text) { return Pattern.prefixedIgnoreCase(name(), text); } @@ -87,7 +87,7 @@ default Restriction startsWithIgnoreCase(String text) { * @param text the substring to match. * @return a Restriction representing a substring `LIKE` condition. */ - default Restriction contains(String text) { + default Pattern contains(String text) { return Pattern.substringed(name(), text); } @@ -97,7 +97,7 @@ default Restriction contains(String text) { * @param text the substring to match. * @return a Restriction representing a case-insensitive substring `LIKE` condition. */ - default Restriction containsIgnoreCase(String text) { + default Pattern containsIgnoreCase(String text) { return Pattern.substringedIgnoreCase(name(), text); } @@ -107,7 +107,7 @@ default Restriction containsIgnoreCase(String text) { * @param text the text suffix to match. * @return a Restriction representing a suffix `LIKE` condition. */ - default Restriction endsWith(String text) { + default Pattern endsWith(String text) { return Pattern.suffixed(name(), text); } @@ -117,7 +117,7 @@ default Restriction endsWith(String text) { * @param text the text suffix to match. * @return a Restriction representing a case-insensitive suffix `LIKE` condition. */ - default Restriction endsWithIgnoreCase(String text) { + default Pattern endsWithIgnoreCase(String text) { return Pattern.suffixedIgnoreCase(name(), text); } From 0265f04a23242d278724582188229265a8c021ed Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 6 Nov 2024 21:10:58 +0000 Subject: [PATCH 23/57] feat: remove unsed imports Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/metamodel/SortableAttribute.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index 8c5397a38..cd7275367 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -20,8 +20,6 @@ import jakarta.data.Operator; import jakarta.data.Sort; -import java.util.List; - /** * Represents a sortable entity attribute in the {@link StaticMetamodel}. * Entity attribute types that are sortable include: From 91f7e287bf6944a7c9f7318fb6d8b38096a8f473 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 7 Nov 2024 18:38:21 +0000 Subject: [PATCH 24/57] feat: update text attribute Signed-off-by: Otavio Santana --- .../java/jakarta/data/metamodel/Pattern.java | 132 ++++++++++-------- .../jakarta/data/metamodel/TextAttribute.java | 43 +----- 2 files changed, 84 insertions(+), 91 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 42b757b43..f627459bb 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -65,90 +65,112 @@ public String value() { } /** - * Creates a case-sensitive pattern match for an exact match. + * Creates a pattern for an exact `LIKE` match on the specified value. + * This method sets the field to `null`, allowing it to be applied + * later to a specific attribute. * - * @param field the field to apply the pattern on. - * @param value the value to match exactly. - * @return a Pattern for an exact match. + *

Example usage:

+ *
+     * Restriction titlePattern = _Book.title.like(Pattern.like("Jakarta"));
+     * 
+ * + * @param value the exact text to match. + * @return a Pattern instance for an exact match. */ - public static Pattern like(String field, String value) { - return new Pattern<>(field, value, false); + public static Pattern like(String value) { + return like(null, value); } /** - * Creates a case-insensitive pattern match for an exact match. + * Creates a pattern for a `LIKE` match where values start with the specified prefix. + * The `field` is set to `null` initially, allowing it to be assigned to an attribute later. * - * @param field the field to apply the pattern on. - * @param value the value to match exactly. - * @return a Pattern for a case-insensitive exact match. + *

Example usage:

+ *
+     * Restriction titlePattern = _Book.title.like(Pattern.startsWith("Hibernate"));
+     * 
+ * + * @param value the prefix to match at the beginning of the field's value. + * @return a Pattern instance for a prefix match. */ - public static Pattern likeIgnoreCase(String field, String value) { - return new Pattern<>(field, value, true); + public static Pattern startsWith(String value) { + return startsWith(null, value); } /** - * Creates a pattern match for values prefixed with the specified value. + * Creates a pattern for a `LIKE` match where values contain the specified substring. + * This method initializes the field to `null`, allowing the pattern to be applied to + * a specific attribute later. * - * @param field the field to apply the pattern on. - * @param value the prefix to match. - * @return a Pattern for prefix matching. + *

Example usage:

+ *
+     * Restriction descriptionPattern = _Book.description.like(Pattern.contains("Java"));
+     * 
+ * + * @param value the substring to match within the field's value. + * @return a Pattern instance for a substring match. */ - public static Pattern prefixed(String field, String value) { - return new Pattern<>(field, value + "%", false); + public static Pattern contains(String value) { + return contains(null, value); } /** - * Creates a case-insensitive pattern match for values prefixed with the specified value. + * Creates a pattern for a `LIKE` match where values end with the specified suffix. + * The field is set to `null`, allowing assignment to a specific attribute later. * - * @param field the field to apply the pattern on. - * @param value the prefix to match. - * @return a Pattern for case-insensitive prefix matching. + *

Example usage:

+ *
+     * Restriction titlePattern = _Book.title.like(Pattern.endsWith("Guide"));
+     * 
+ * + * @param value the suffix to match at the end of the field's value. + * @return a Pattern instance for a suffix match. */ - public static Pattern prefixedIgnoreCase(String field, String value) { - return new Pattern<>(field, value + "%", true); + public static Pattern endsWith(String value) { + return endsWith(null, value); } - /** - * Creates a pattern match for values containing the specified substring. - * - * @param field the field to apply the pattern on. - * @param value the substring to match. - * @return a Pattern for substring matching. - */ - public static Pattern substringed(String field, String value) { + static Pattern endsWith(String field, String value) { + return endsWith(field, value); + } + + static Pattern contains(String field, String value) { return new Pattern<>(field, "%" + value + "%", false); } - /** - * Creates a case-insensitive pattern match for values containing the specified substring. - * - * @param field the field to apply the pattern on. - * @param value the substring to match. - * @return a Pattern for case-insensitive substring matching. - */ - public static Pattern substringedIgnoreCase(String field, String value) { - return new Pattern<>(field, "%" + value + "%", true); + static Pattern startsWith(String field, String value) { + return new Pattern<>(field, value + "%", false); } - /** - * Creates a pattern match for values suffixed with the specified value. - * - * @param field the field to apply the pattern on. - * @param value the suffix to match. - * @return a Pattern for suffix matching. - */ - public static Pattern suffixed(String field, String value) { + static Pattern suffixed(String field, String value) { return new Pattern<>(field, "%" + value, false); } + static Pattern substringed(String field, String value) { + return new Pattern<>(field, "%" + value + "%", false); + } + + static Pattern like(String field, String value) { + return new Pattern<>(field, value, false); + } + + static Pattern prefixed(String field, String value) { + return new Pattern<>(field, value + "%", false); + } + /** - * Creates a case-insensitive pattern match for values suffixed with the specified value. + * Returns a new `Pattern` instance with case-insensitive matching enabled. + * This method allows you to specify that the pattern should ignore case when matching. + * + *

Example usage:

+ *
+     * // Case-insensitive prefix match
+     * Restriction titlePattern = _Book.title.like(Pattern.startsWith("Hibernate").ignoringCase());
+     * 
* - * @param field the field to apply the pattern on. - * @param value the suffix to match. - * @return a Pattern for case-insensitive suffix matching. + * @return a new Pattern instance with `ignoreCase` set to `true`. */ - public static Pattern suffixedIgnoreCase(String field, String value) { - return new Pattern<>(field, "%" + value, true); + public Pattern ignoringCase() { + return new Pattern<>(field, value, true); } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 330415721..cd9e71cec 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -52,13 +52,13 @@ default Pattern like(String text) { } /** - * Creates a case-insensitive `LIKE` restriction for an exact match on the specified text. + * Creates a `LIKE` restriction for a match on the specified text. * - * @param text the text to match exactly. - * @return a Restriction representing a case-insensitive `LIKE` condition. + * @param pattern the text to match. + * @return a Restriction representing a `LIKE` condition. */ - default Pattern likeIgnoreCase(String text) { - return Pattern.likeIgnoreCase(name(), text); + default Pattern like(Pattern pattern) { + return new Pattern<>(name(), pattern.value(), pattern.ignoreCase()); } /** @@ -71,16 +71,6 @@ default Pattern startsWith(String text) { return Pattern.prefixed(name(), text); } - /** - * Creates a case-insensitive `LIKE` restriction for values that start with the specified text. - * - * @param text the text prefix to match. - * @return a Restriction representing a case-insensitive prefix `LIKE` condition. - */ - default Pattern startsWithIgnoreCase(String text) { - return Pattern.prefixedIgnoreCase(name(), text); - } - /** * Creates a `LIKE` restriction for values that contain the specified substring. * @@ -88,18 +78,9 @@ default Pattern startsWithIgnoreCase(String text) { * @return a Restriction representing a substring `LIKE` condition. */ default Pattern contains(String text) { - return Pattern.substringed(name(), text); + return Pattern.contains(name(), text); } - /** - * Creates a case-insensitive `LIKE` restriction for values that contain the specified substring. - * - * @param text the substring to match. - * @return a Restriction representing a case-insensitive substring `LIKE` condition. - */ - default Pattern containsIgnoreCase(String text) { - return Pattern.substringedIgnoreCase(name(), text); - } /** * Creates a `LIKE` restriction for values that end with the specified text. @@ -108,17 +89,7 @@ default Pattern containsIgnoreCase(String text) { * @return a Restriction representing a suffix `LIKE` condition. */ default Pattern endsWith(String text) { - return Pattern.suffixed(name(), text); - } - - /** - * Creates a case-insensitive `LIKE` restriction for values that end with the specified text. - * - * @param text the text suffix to match. - * @return a Restriction representing a case-insensitive suffix `LIKE` condition. - */ - default Pattern endsWithIgnoreCase(String text) { - return Pattern.suffixedIgnoreCase(name(), text); + return Pattern.endsWith(name(), text); } } From bd8b5b886d5f8fcc0cf029538208b71f0c3f661b Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 7 Nov 2024 18:40:20 +0000 Subject: [PATCH 25/57] feat: update pattern Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/metamodel/TextAttribute.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index cd9e71cec..d4a6b3c63 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -52,9 +52,9 @@ default Pattern like(String text) { } /** - * Creates a `LIKE` restriction for a match on the specified text. + * Creates a `LIKE` restriction for a match on the specified pattern. * - * @param pattern the text to match. + * @param pattern the `Pattern` instance to match. * @return a Restriction representing a `LIKE` condition. */ default Pattern like(Pattern pattern) { From e5bdf6e2d7947bab57391ced6567d833e03a4f2f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 7 Nov 2024 19:04:25 +0000 Subject: [PATCH 26/57] feat: create restriction Signed-off-by: Otavio Santana --- .../java/jakarta/data/LogicalOperator.java | 28 ----------- .../jakarta/data/metamodel/Attribute.java | 4 +- .../data/metamodel/BasicRestriction.java | 37 +++++++++----- .../data/metamodel/CompositeRestriction.java | 14 ++---- .../data/metamodel/MultipleRestriction.java | 49 +++++++++++++++++++ .../java/jakarta/data/metamodel/Pattern.java | 2 +- .../java/jakarta/data/metamodel/Range.java | 2 +- .../java/jakarta/data/metamodel/Restrict.java | 43 ++++++++++++++++ .../jakarta/data/metamodel/Restriction.java | 46 +++-------------- .../data/metamodel/SimpleRestriction.java | 42 ++++++++++++++++ .../data/metamodel/SortableAttribute.java | 8 +-- 11 files changed, 179 insertions(+), 96 deletions(-) delete mode 100644 api/src/main/java/jakarta/data/LogicalOperator.java create mode 100644 api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java create mode 100644 api/src/main/java/jakarta/data/metamodel/Restrict.java create mode 100644 api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java diff --git a/api/src/main/java/jakarta/data/LogicalOperator.java b/api/src/main/java/jakarta/data/LogicalOperator.java deleted file mode 100644 index f512f16a4..000000000 --- a/api/src/main/java/jakarta/data/LogicalOperator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data; - -import jakarta.data.metamodel.Restriction; - -/** - * Enum representing logical operators for combining multiple {@link Restriction} objects. - */ -public enum LogicalOperator { - AND, - OR -} diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index b54ab8ad4..487d20c64 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -41,7 +41,7 @@ public interface Attribute { * @return a Restriction representing an equality condition. */ default Restriction equal(Object value) { - return new BasicRestriction<>(name(), Operator.EQUAL, value); + return new SimpleRestriction<>(name(), Operator.EQUAL, value); } /** @@ -50,6 +50,6 @@ default Restriction equal(Object value) { * @return a Restriction representing the condition where the attribute is null. */ default Restriction isNull(){ - return new BasicRestriction<>(name(), Operator.EQUAL, null); + return new SimpleRestriction<>(name(), Operator.EQUAL, null); } } diff --git a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java index 750f6a425..bff47243f 100644 --- a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java @@ -20,23 +20,36 @@ import jakarta.data.Operator; /** - * A basic implementation of the `Restriction` interface, representing various conditions - * used in repository queries, including equality, comparison, and range checks. + * A basic restriction applied to a single field, representing conditions such as equality, + * comparisons, range checks, and pattern matches. + * + *

The `BasicRestriction` interface provides methods for defining simple, singular restrictions + * based on a specific field, an operator, and an optional comparison value. This interface supports + * common operators (e.g., EQUAL, GREATER_THAN) and serves as a foundation for filtering + * logic on individual fields.

* * @param the type of the entity on which the restriction is applied. */ -public record BasicRestriction(String field, Operator operator, Object value) implements Restriction { +public interface BasicRestriction extends Restriction { + + /** + * The name of the field on which this restriction is applied. + * + * @return the field name as a String. + */ + String field(); + + /** + * The operator defining the type of comparison or condition for this restriction. + * + * @return the operator representing the restriction type (e.g., EQUAL, LIKE, BETWEEN). + */ + Operator operator(); /** - * Constructs a `BasicRestriction` with the specified field, operator, and value. + * The value used for comparison in this restriction, if applicable. * - * @param field the name of the field to apply the restriction to. - * @param operator the operator defining the comparison or condition. - * @param value the value to compare the field against (optional for null checks). + * @return the comparison value, or {@code null} if the restriction does not use a value (e.g., IS_NULL). */ - public BasicRestriction { - if (field == null || operator == null) { - throw new IllegalArgumentException("Field and operator must not be null."); - } - } + Object value(); } diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java index db944e244..7c34c9011 100644 --- a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java @@ -17,26 +17,23 @@ */ package jakarta.data.metamodel; -import jakarta.data.LogicalOperator; - import java.util.Iterator; import java.util.List; /** * A composite restriction representing a collection of individual {@link Restriction} - * and {@link LogicalOperator} instances, combined under a single logical operation. + * and {@link Restrict} instances, combined under a single logical operation. * *

This record allows multiple restrictions to be treated as a single entity, making * it easy to pass complex conditions to repository methods.

* * @param the entity type that the restrictions apply to. */ -public record CompositeRestriction(LogicalOperator operator, List> restrictions) implements Iterable> { +public record CompositeRestriction(Restrict type, List> restrictions) implements Iterable>, MultipleRestriction { /** * Constructs a composite restriction with the specified operator and list of restrictions. * - * @param operator the logical operator (AND or OR) to apply between the restrictions. * @param restrictions the list of restrictions to combine. */ public CompositeRestriction { @@ -44,7 +41,7 @@ public record CompositeRestriction(LogicalOperator operator, List> iterator() { + public Iterator> iterator() { return restrictions.iterator(); } @@ -57,7 +54,7 @@ public Iterator> iterator() { */ @SafeVarargs public static CompositeRestriction all(Restriction... restrictions) { - return new CompositeRestriction<>(LogicalOperator.AND, List.of(restrictions)); + return new CompositeRestriction<>(Restrict.ALL, List.of(restrictions)); } /** @@ -69,8 +66,7 @@ public static CompositeRestriction all(Restriction... restrictions) { */ @SafeVarargs public static CompositeRestriction any(Restriction... restrictions) { - return new CompositeRestriction<>(LogicalOperator.OR, List.of(restrictions)); + return new CompositeRestriction<>(Restrict.ANY, List.of(restrictions)); } - } diff --git a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java new file mode 100644 index 000000000..b703b1f04 --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import java.util.List; + +/** + * A composite restriction that combines multiple `Restriction` instances using logical operators. + * + *

The `MultipleRestriction` interface allows for combining multiple restrictions, enabling complex + * filtering scenarios where multiple conditions must be satisfied. Each contained `Restriction` + * can be evaluated based on the logical operator specified by the `Restrict` type.

+ * + *

This interface is useful for defining AND/OR conditions where multiple fields and restrictions + * are evaluated together in a repository query.

+ * + * @param the type of the entity on which the restriction is applied. + */ +public interface MultipleRestriction extends Restriction { + + /** + * The list of restrictions that are combined in this composite restriction. + * + * @return a list of individual restrictions. + */ + List> restrictions(); + + /** + * The logical operator used to combine the contained restrictions, such as AND or OR. + * + * @return the logical combination type for this composite restriction. + */ + Restrict type(); +} diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index f627459bb..f482d8bf5 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -42,7 +42,7 @@ * * @param the type of the entity on which the restriction is applied. */ -public record Pattern(String field, String value, boolean ignoreCase) implements Restriction { +public record Pattern(String field, String value, boolean ignoreCase) implements BasicRestriction { /** * The operator for pattern-based restrictions, fixed as `LIKE`. diff --git a/api/src/main/java/jakarta/data/metamodel/Range.java b/api/src/main/java/jakarta/data/metamodel/Range.java index 65b8bdc4e..b13cd6b8d 100644 --- a/api/src/main/java/jakarta/data/metamodel/Range.java +++ b/api/src/main/java/jakarta/data/metamodel/Range.java @@ -51,7 +51,7 @@ * * @param the type of the entity's attribute on which the range is applied. */ -public record Range(String field, T lowerBound, T upperBound, boolean open) implements Restriction { +public record Range(String field, T lowerBound, T upperBound, boolean open) implements BasicRestriction { @Override public String field() { diff --git a/api/src/main/java/jakarta/data/metamodel/Restrict.java b/api/src/main/java/jakarta/data/metamodel/Restrict.java new file mode 100644 index 000000000..ff1cfe2c6 --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/Restrict.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + + +/** + * Represents the logical operators used to combine multiple restrictions in a query. + * + *

The `Restrict` enum defines two types of logical combinations:

+ *
    + *
  • ALL - Requires that all contained restrictions are satisfied (logical AND).
  • + *
  • ANY - Requires that at least one of the contained restrictions is satisfied (logical OR).
  • + *
+ * + *

This enum is typically used in {@link MultipleRestriction} to specify how its list of + * restrictions should be combined, allowing for flexible and complex query conditions.

+ */ +public enum Restrict { + /** + * Requires that all contained restrictions must be satisfied (logical AND). + */ + ALL, + + /** + * Requires that at least one of the contained restrictions must be satisfied (logical OR). + */ + ANY +} diff --git a/api/src/main/java/jakarta/data/metamodel/Restriction.java b/api/src/main/java/jakarta/data/metamodel/Restriction.java index 16dfc23cc..9a6551da8 100644 --- a/api/src/main/java/jakarta/data/metamodel/Restriction.java +++ b/api/src/main/java/jakarta/data/metamodel/Restriction.java @@ -17,54 +17,22 @@ */ package jakarta.data.metamodel; -import jakarta.data.Operator; /** * Represents a condition used to filter values in repository queries. * - *

The `Restriction` interface defines various types of conditions, including equality, - * comparison, range, null checks, and pattern matching checks, to support flexible and - * type-safe filtering.

+ *

The `Restriction` interface serves as a general contract for defining filter conditions, + * supporting various operations such as equality, comparisons, range, null checks, + * and pattern matching. Implementations of `Restriction` can be used to construct + * flexible and type-safe filtering logic in repository queries.

* - *

Static factory methods are provided to create instances of `Restriction` with - * specific conditions, using `BasicRestriction` as the underlying implementation.

- * - *
- * Restriction titleEquals = Restriction.equal("title", "Jakarta Data");
- *
- * Restriction ratingGreaterThan = Restriction.greaterThan("rating", 4.5);
- *
- * Restriction publicationDateRange = Restriction.between("publicationDate", pastDate, LocalDate.now());
- *
- * Restriction authorIsNull = Restriction.isNull("author");
- *
- * Restriction titleStartsWith = Restriction.like("title", "Jakarta%");
- *
- * Restriction titleIgnoreCase = Restriction.like("title", Pattern.prefixedIgnoreCase("Java"));
- * 
+ *

Subtypes include {@link BasicRestriction}, which handles single-field conditions, + * and {@link MultipleRestriction}, which combines multiple restrictions + * using logical operators.

* * @param the type of the entity on which the restriction is applied. */ public interface Restriction { - /** - * The name of the field on which this restriction is applied. - * - * @return the field name. - */ - String field(); - - /** - * The operator for this restriction. - * - * @return the operator defining the restriction type. - */ - Operator operator(); - /** - * The value used in this restriction, if applicable. - * - * @return the comparison value, or null if the restriction does not use a value. - */ - Object value(); } diff --git a/api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java b/api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java new file mode 100644 index 000000000..fb2f10607 --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import jakarta.data.Operator; + +/** + * A basic implementation of the `Restriction` interface, representing various conditions + * used in repository queries, including equality, comparison, and range checks. + * + * @param the type of the entity on which the restriction is applied. + */ +public record SimpleRestriction(String field, Operator operator, Object value) implements Restriction { + + /** + * Constructs a `BasicRestriction` with the specified field, operator, and value. + * + * @param field the name of the field to apply the restriction to. + * @param operator the operator defining the comparison or condition. + * @param value the value to compare the field against (optional for null checks). + */ + public SimpleRestriction { + if (field == null || operator == null) { + throw new IllegalArgumentException("Field and operator must not be null."); + } + } +} diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index cd7275367..ba3b770ba 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -57,7 +57,7 @@ public interface SortableAttribute extends Attribute { * @return a Restriction representing a greater-than condition. */ default Restriction greaterThan(Object value) { - return new BasicRestriction<>(name(), Operator.GREATER_THAN, value); + return new SimpleRestriction<>(name(), Operator.GREATER_THAN, value); } /** @@ -67,7 +67,7 @@ default Restriction greaterThan(Object value) { * @return a Restriction representing a greater-than-or-equal condition. */ default Restriction greaterThanOrEqual(Object value) { - return new BasicRestriction<>(name(), Operator.GREATER_THAN_EQUAL, value); + return new SimpleRestriction<>(name(), Operator.GREATER_THAN_EQUAL, value); } /** @@ -77,7 +77,7 @@ default Restriction greaterThanOrEqual(Object value) { * @return a Restriction representing a less-than condition. */ default Restriction lessThan(Object value) { - return new BasicRestriction<>(name(), Operator.LESS_THAN, value); + return new SimpleRestriction<>(name(), Operator.LESS_THAN, value); } /** @@ -87,7 +87,7 @@ default Restriction lessThan(Object value) { * @return a Restriction representing a less-than-or-equal condition. */ default Restriction lessThanOrEqual(Object value) { - return new BasicRestriction<>(name(), Operator.LESS_THAN_EQUAL, value); + return new SimpleRestriction<>(name(), Operator.LESS_THAN_EQUAL, value); } /** From d6010caf8c2255f537a1f039b9c66c23c6e3beec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:14:37 +0000 Subject: [PATCH 27/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index f482d8bf5..eadb7fa73 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -159,7 +159,7 @@ static Pattern prefixed(String field, String value) { } /** - * Returns a new `Pattern` instance with case-insensitive matching enabled. + * Returns a new {@code Pattern} instance with case-insensitive matching. * This method allows you to specify that the pattern should ignore case when matching. * *

Example usage:

From 3c4369bb14566c81726695170cacfc697ab0e667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:14:47 +0000 Subject: [PATCH 28/57] Update api/src/main/java/jakarta/data/metamodel/BasicRestriction.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/BasicRestriction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java index bff47243f..cf15e251b 100644 --- a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java @@ -23,7 +23,7 @@ * A basic restriction applied to a single field, representing conditions such as equality, * comparisons, range checks, and pattern matches. * - *

The `BasicRestriction` interface provides methods for defining simple, singular restrictions + *

The {@code BasicRestriction} interface provides methods for defining simple, singular restrictions * based on a specific field, an operator, and an optional comparison value. This interface supports * common operators (e.g., EQUAL, GREATER_THAN) and serves as a foundation for filtering * logic on individual fields.

From d0837adb273fdcd1f11f2a52dbcbab3c62297d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:14:54 +0000 Subject: [PATCH 29/57] Update api/src/main/java/jakarta/data/metamodel/BasicRestriction.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/BasicRestriction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java index cf15e251b..bb2ac0671 100644 --- a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java @@ -20,7 +20,7 @@ import jakarta.data.Operator; /** - * A basic restriction applied to a single field, representing conditions such as equality, + * A basic restriction applies to a single field, representing conditions such as equality, * comparisons, range checks, and pattern matches. * *

The {@code BasicRestriction} interface provides methods for defining simple, singular restrictions From 5f0d8579d30c6e812651c157d27635ee397236b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:15:12 +0000 Subject: [PATCH 30/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index eadb7fa73..11612cbc3 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -65,7 +65,7 @@ public String value() { } /** - * Creates a pattern for an exact `LIKE` match on the specified value. + * Creates a pattern for a {@link Operator#LIKE LIKE} match on the specified value. * This method sets the field to `null`, allowing it to be applied * later to a specific attribute. * From 97e2dbe88606293254ddc1b1a559d3e4978f4683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:15:19 +0000 Subject: [PATCH 31/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 11612cbc3..da3b3c4c5 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -74,8 +74,8 @@ public String value() { * Restriction titlePattern = _Book.title.like(Pattern.like("Jakarta")); * * - * @param value the exact text to match. - * @return a Pattern instance for an exact match. + * @param value the pattern to match. + * @return a Pattern instance for a pattern match. */ public static Pattern like(String value) { return like(null, value); From d332f7efba41d3f8b2aaf906ff7623558408649b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:15:28 +0000 Subject: [PATCH 32/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index da3b3c4c5..5b0ad7f9b 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -77,8 +77,8 @@ public String value() { * @param value the pattern to match. * @return a Pattern instance for a pattern match. */ - public static Pattern like(String value) { - return like(null, value); + public static Pattern like(String pattern) { + return like(null, pattern); } /** From 1fff4892ab363059804462138d1ec31945397a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:15:36 +0000 Subject: [PATCH 33/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 5b0ad7f9b..5461c54ea 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -71,7 +71,7 @@ public String value() { * *

Example usage:

*
-     * Restriction titlePattern = _Book.title.like(Pattern.like("Jakarta"));
+     * Restriction titlePattern = _Book.title.like(Pattern.like("Jakarta %"));
      * 
* * @param value the pattern to match. From 1b072f6594c80b074c985d6769c05b62fa88488e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:16:04 +0000 Subject: [PATCH 34/57] Update api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java Co-authored-by: Nathan Rauh --- .../main/java/jakarta/data/metamodel/MultipleRestriction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java index b703b1f04..a6df5a002 100644 --- a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java @@ -20,7 +20,7 @@ import java.util.List; /** - * A composite restriction that combines multiple `Restriction` instances using logical operators. + * A composite restriction that combines multiple {@link Restriction} instances using logical operators. * *

The `MultipleRestriction` interface allows for combining multiple restrictions, enabling complex * filtering scenarios where multiple conditions must be satisfied. Each contained `Restriction` From 5d429a2023fe74e89e44fd75e4909e61b8758e42 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 7 Nov 2024 20:22:46 +0000 Subject: [PATCH 35/57] feat: reduce the scope with is annotation Signed-off-by: Otavio Santana --- .../main/java/jakarta/data/repository/Is.java | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 api/src/main/java/jakarta/data/repository/Is.java diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java deleted file mode 100644 index b06de2880..000000000 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data.repository; - -import jakarta.data.Operator; - -/** - * Annotation to specify query conditions on method parameters in Jakarta Data repository interfaces. - * The {@code @Is} annotation, combined with the {@link Operator} enum, enables flexible and expressive - * filtering in repository queries. - * Usage Example: - *

{@code
- * @Find
- * Page search(@Is(Operator.Equal) String name); // Finds products where the name matches exactly.
- * }
- * - */ -public @interface Is { - - /** - * Defines the condition to apply to the annotated parameter. - * - * @return the specified {@link Operator} value. - */ - Operator value(); -} From e37834d9509d196e620e3a5525f5fe0391bd82bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Thu, 7 Nov 2024 20:26:36 +0000 Subject: [PATCH 36/57] Update api/src/main/java/jakarta/data/metamodel/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/metamodel/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java index 5461c54ea..377fc1b1a 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java @@ -23,7 +23,7 @@ /** * Represents a pattern-based restriction for matching operations, encapsulating different * options such as prefix, suffix, and substring matching. This implementation - * allows flexibility in creating pattern-based conditions directly as `Restriction` instances. + * allows flexibility in creating pattern-based conditions directly as {@link Restriction} instances. * *

Example usage with metadata attributes:

*

From 11e048a95410026ac7ebc50c83de6ca6d4dff979 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= 
Date: Thu, 7 Nov 2024 20:26:58 +0000
Subject: [PATCH 37/57] Update
 api/src/main/java/jakarta/data/metamodel/Pattern.java

Co-authored-by: Nathan Rauh 
---
 api/src/main/java/jakarta/data/metamodel/Pattern.java | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java
index 377fc1b1a..df39b852e 100644
--- a/api/src/main/java/jakarta/data/metamodel/Pattern.java
+++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java
@@ -27,9 +27,6 @@
  *
  * 

Example usage with metadata attributes:

*
- * // Case-sensitive exact match
- * Restriction exactMatch = Pattern.like(_Book.title, "Jakarta Data");
- *
  * // Case-insensitive prefix match
  * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase(_Book.title, "Jak");
  *

From 6b9d1027d7e7f71f3bdbe9838ae9fe59209a36b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= 
Date: Thu, 7 Nov 2024 20:27:10 +0000
Subject: [PATCH 38/57] Update
 api/src/main/java/jakarta/data/metamodel/Pattern.java

Co-authored-by: Nathan Rauh 
---
 api/src/main/java/jakarta/data/metamodel/Pattern.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java
index df39b852e..e82bda7c7 100644
--- a/api/src/main/java/jakarta/data/metamodel/Pattern.java
+++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java
@@ -30,7 +30,7 @@
  * // Case-insensitive prefix match
  * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase(_Book.title, "Jak");
  *
- * // Case-sensitive suffix match
+ * // Suffix match
  * Restriction suffixMatch = Pattern.suffixed(_Book.title, "Guide");
  *
  * // Case-insensitive substring match

From 225a806f50f83e4715694e9a44c64dffe74ec04e Mon Sep 17 00:00:00 2001
From: Otavio Santana 
Date: Thu, 7 Nov 2024 20:32:42 +0000
Subject: [PATCH 39/57] docs: fix pattern documentation

Signed-off-by: Otavio Santana 
---
 .../java/jakarta/data/metamodel/Pattern.java  | 40 +------------------
 1 file changed, 2 insertions(+), 38 deletions(-)

diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/metamodel/Pattern.java
index e82bda7c7..98be1fe87 100644
--- a/api/src/main/java/jakarta/data/metamodel/Pattern.java
+++ b/api/src/main/java/jakarta/data/metamodel/Pattern.java
@@ -27,14 +27,7 @@
  *
  * 

Example usage with metadata attributes:

*
- * // Case-insensitive prefix match
- * Restriction prefixIgnoreCase = Pattern.prefixedIgnoreCase(_Book.title, "Jak");
- *
- * // Suffix match
- * Restriction suffixMatch = Pattern.suffixed(_Book.title, "Guide");
- *
- * // Case-insensitive substring match
- * Restriction substringIgnoreCase = Pattern.substringedIgnoreCase(_Book.title, "Java");
+ * Restriction prefixIgnoreCase = _Book.title.endsWith("Guide");
  * 
* * @param the type of the entity on which the restriction is applied. @@ -66,12 +59,6 @@ public String value() { * This method sets the field to `null`, allowing it to be applied * later to a specific attribute. * - *

Example usage:

- *
-     * Restriction titlePattern = _Book.title.like(Pattern.like("Jakarta %"));
-     * 
- * - * @param value the pattern to match. * @return a Pattern instance for a pattern match. */ public static Pattern like(String pattern) { @@ -82,11 +69,6 @@ public static Pattern like(String pattern) { * Creates a pattern for a `LIKE` match where values start with the specified prefix. * The `field` is set to `null` initially, allowing it to be assigned to an attribute later. * - *

Example usage:

- *
-     * Restriction titlePattern = _Book.title.like(Pattern.startsWith("Hibernate"));
-     * 
- * * @param value the prefix to match at the beginning of the field's value. * @return a Pattern instance for a prefix match. */ @@ -99,11 +81,6 @@ public static Pattern startsWith(String value) { * This method initializes the field to `null`, allowing the pattern to be applied to * a specific attribute later. * - *

Example usage:

- *
-     * Restriction descriptionPattern = _Book.description.like(Pattern.contains("Java"));
-     * 
- * * @param value the substring to match within the field's value. * @return a Pattern instance for a substring match. */ @@ -115,10 +92,6 @@ public static Pattern contains(String value) { * Creates a pattern for a `LIKE` match where values end with the specified suffix. * The field is set to `null`, allowing assignment to a specific attribute later. * - *

Example usage:

- *
-     * Restriction titlePattern = _Book.title.like(Pattern.endsWith("Guide"));
-     * 
* * @param value the suffix to match at the end of the field's value. * @return a Pattern instance for a suffix match. @@ -139,14 +112,6 @@ static Pattern startsWith(String field, String value) { return new Pattern<>(field, value + "%", false); } - static Pattern suffixed(String field, String value) { - return new Pattern<>(field, "%" + value, false); - } - - static Pattern substringed(String field, String value) { - return new Pattern<>(field, "%" + value + "%", false); - } - static Pattern like(String field, String value) { return new Pattern<>(field, value, false); } @@ -159,10 +124,9 @@ static Pattern prefixed(String field, String value) { * Returns a new {@code Pattern} instance with case-insensitive matching. * This method allows you to specify that the pattern should ignore case when matching. * - *

Example usage:

*
      * // Case-insensitive prefix match
-     * Restriction titlePattern = _Book.title.like(Pattern.startsWith("Hibernate").ignoringCase());
+     * Restriction titlePattern = Pattern.startsWith("Hibernate").ignoringCase();
      * 
* * @return a new Pattern instance with `ignoreCase` set to `true`. From e3a79b5dfca3429e4b458b34466250d9621f73f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Mon, 11 Nov 2024 07:37:10 +0000 Subject: [PATCH 40/57] Update api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java Co-authored-by: Nathan Rauh --- .../java/jakarta/data/metamodel/MultipleRestriction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java index a6df5a002..0c818ccae 100644 --- a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java @@ -22,9 +22,9 @@ /** * A composite restriction that combines multiple {@link Restriction} instances using logical operators. * - *

The `MultipleRestriction` interface allows for combining multiple restrictions, enabling complex - * filtering scenarios where multiple conditions must be satisfied. Each contained `Restriction` - * can be evaluated based on the logical operator specified by the `Restrict` type.

+ *

The {@code MultipleRestriction} interface allows for combining multiple restrictions, enabling complex + * filtering scenarios where multiple conditions must be satisfied. Each contained {@link Restriction} + * can be evaluated based on the logical operator specified by the {@link Restrict} type.

* *

This interface is useful for defining AND/OR conditions where multiple fields and restrictions * are evaluated together in a repository query.

From e3c8d873c7f5cf0cf139b3c84130448e5d4af7fc Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 12 Nov 2024 09:02:33 +0000 Subject: [PATCH 41/57] feat: moved text attribute Signed-off-by: Otavio Santana --- .../jakarta/data/{metamodel => }/Pattern.java | 67 ++++--------------- .../jakarta/data/metamodel/TextAttribute.java | 28 +++++--- .../impl/BasicRestrictionRecord.java | 25 +++++++ api/src/main/java/module-info.java | 1 + 4 files changed, 56 insertions(+), 65 deletions(-) rename api/src/main/java/jakarta/data/{metamodel => }/Pattern.java (62%) create mode 100644 api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java diff --git a/api/src/main/java/jakarta/data/metamodel/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java similarity index 62% rename from api/src/main/java/jakarta/data/metamodel/Pattern.java rename to api/src/main/java/jakarta/data/Pattern.java index 98be1fe87..134f6f086 100644 --- a/api/src/main/java/jakarta/data/metamodel/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -15,9 +15,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.metamodel; +package jakarta.data; -import jakarta.data.Operator; +import jakarta.data.metamodel.Restriction; /** @@ -30,29 +30,8 @@ * Restriction prefixIgnoreCase = _Book.title.endsWith("Guide"); *
* - * @param the type of the entity on which the restriction is applied. */ -public record Pattern(String field, String value, boolean ignoreCase) implements BasicRestriction { - - /** - * The operator for pattern-based restrictions, fixed as `LIKE`. - * - * @return the `LIKE` operator. - */ - @Override - public Operator operator() { - return Operator.LIKE; - } - - /** - * The value of the pattern used for matching. - * - * @return the pattern value. - */ - @Override - public String value() { - return value; - } +public record Pattern(String value, boolean ignoreCase) { /** * Creates a pattern for a {@link Operator#LIKE LIKE} match on the specified value. @@ -61,8 +40,8 @@ public String value() { * * @return a Pattern instance for a pattern match. */ - public static Pattern like(String pattern) { - return like(null, pattern); + public static Pattern like(String pattern) { + return new Pattern(pattern, false); } /** @@ -72,8 +51,8 @@ public static Pattern like(String pattern) { * @param value the prefix to match at the beginning of the field's value. * @return a Pattern instance for a prefix match. */ - public static Pattern startsWith(String value) { - return startsWith(null, value); + public static Pattern startsWith(String value) { + return new Pattern( value + "%", false); } /** @@ -84,8 +63,8 @@ public static Pattern startsWith(String value) { * @param value the substring to match within the field's value. * @return a Pattern instance for a substring match. */ - public static Pattern contains(String value) { - return contains(null, value); + public static Pattern contains(String value) { + return new Pattern("%" + value + "%", false); } /** @@ -96,28 +75,8 @@ public static Pattern contains(String value) { * @param value the suffix to match at the end of the field's value. * @return a Pattern instance for a suffix match. */ - public static Pattern endsWith(String value) { - return endsWith(null, value); - } - - static Pattern endsWith(String field, String value) { - return endsWith(field, value); - } - - static Pattern contains(String field, String value) { - return new Pattern<>(field, "%" + value + "%", false); - } - - static Pattern startsWith(String field, String value) { - return new Pattern<>(field, value + "%", false); - } - - static Pattern like(String field, String value) { - return new Pattern<>(field, value, false); - } - - static Pattern prefixed(String field, String value) { - return new Pattern<>(field, value + "%", false); + public static Pattern endsWith(String value) { + return new Pattern(value + "%", false); } /** @@ -131,7 +90,7 @@ static Pattern prefixed(String field, String value) { * * @return a new Pattern instance with `ignoreCase` set to `true`. */ - public Pattern ignoringCase() { - return new Pattern<>(field, value, true); + public Pattern ignoringCase() { + return new Pattern(value, true); } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index d4a6b3c63..57b4f7114 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -17,11 +17,14 @@ */ package jakarta.data.metamodel; +import jakarta.data.Operator; +import jakarta.data.Pattern; import jakarta.data.Sort; +import jakarta.data.metamodel.impl.BasicRestrictionRecord; /** - * Represents an textual entity attribute in the {@link StaticMetamodel}. + * Represents a textual entity attribute in the {@link StaticMetamodel}. * * @param entity class of the static metamodel. */ @@ -47,8 +50,8 @@ public interface TextAttribute extends SortableAttribute { * @param text the text to match. * @return a Restriction representing a `LIKE` condition. */ - default Pattern like(String text) { - return Pattern.like(name(), text); + default Restriction like(String text) { + return new BasicRestrictionRecord<>(name(), Operator.LIKE, text); } /** @@ -57,8 +60,8 @@ default Pattern like(String text) { * @param pattern the `Pattern` instance to match. * @return a Restriction representing a `LIKE` condition. */ - default Pattern like(Pattern pattern) { - return new Pattern<>(name(), pattern.value(), pattern.ignoreCase()); + default Restriction like(Pattern pattern) { + return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); } /** @@ -67,8 +70,9 @@ default Pattern like(Pattern pattern) { * @param text the text prefix to match. * @return a Restriction representing a prefix `LIKE` condition. */ - default Pattern startsWith(String text) { - return Pattern.prefixed(name(), text); + default Restriction startsWith(String text) { + var pattern = Pattern.startsWith(text); + return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); } /** @@ -77,8 +81,9 @@ default Pattern startsWith(String text) { * @param text the substring to match. * @return a Restriction representing a substring `LIKE` condition. */ - default Pattern contains(String text) { - return Pattern.contains(name(), text); + default Restriction contains(String text) { + var pattern = Pattern.contains(text); + return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); } @@ -88,8 +93,9 @@ default Pattern contains(String text) { * @param text the text suffix to match. * @return a Restriction representing a suffix `LIKE` condition. */ - default Pattern endsWith(String text) { - return Pattern.endsWith(name(), text); + default Restriction endsWith(String text) { + var pattern = Pattern.endsWith(text); + return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); } } diff --git a/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java new file mode 100644 index 000000000..2c7b1987f --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel.impl; + +import jakarta.data.Operator; +import jakarta.data.metamodel.BasicRestriction; + +public record BasicRestrictionRecord(String field, Operator operator, Object value) implements BasicRestriction { + +} diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index e6980ccf2..f82c60db7 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -1076,4 +1076,5 @@ opens jakarta.data.repository; exports jakarta.data.spi; opens jakarta.data.metamodel; + opens jakarta.data; } From 97586f04dfa3f831314b89473b8ac42aab8563d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ot=C3=A1vio=20Santana?= Date: Wed, 13 Nov 2024 07:20:57 +0000 Subject: [PATCH 42/57] Update api/src/main/java/jakarta/data/Pattern.java Co-authored-by: Nathan Rauh --- api/src/main/java/jakarta/data/Pattern.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index 134f6f086..4006f4e69 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -46,13 +46,12 @@ public static Pattern like(String pattern) { /** * Creates a pattern for a `LIKE` match where values start with the specified prefix. - * The `field` is set to `null` initially, allowing it to be assigned to an attribute later. * - * @param value the prefix to match at the beginning of the field's value. + * @param prefixPattern the prefix pattern to match at the beginning of the field's value. * @return a Pattern instance for a prefix match. */ - public static Pattern startsWith(String value) { - return new Pattern( value + "%", false); + public static Pattern startsWith(String prefixPattern) { + return new Pattern(prefixPattern + "%"); } /** From b5b61f0962339635e7693e783a962c04ab8225d7 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 07:26:42 +0000 Subject: [PATCH 43/57] feat: update pattern structure based on Gavin's point Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Pattern.java | 125 ++++++++++++++------ 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index 4006f4e69..53fd819b5 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -21,75 +21,126 @@ /** - * Represents a pattern-based restriction for matching operations, encapsulating different - * options such as prefix, suffix, and substring matching. This implementation - * allows flexibility in creating pattern-based conditions directly as {@link Restriction} instances. + * Represents a pattern-based restriction for matching operations, supporting options + * such as exact match, prefix, suffix, and substring matching. This implementation + * provides flexibility in creating pattern-based conditions directly as {@link Restriction} instances. * *

Example usage with metadata attributes:

*
- * Restriction prefixIgnoreCase = _Book.title.endsWith("Guide");
- * 
+ * // Match for values starting with "Guide" + * Restriction prefixMatch = _Book.title.startsWith("Guide"); * + * // Match for values containing "Java" + * Restriction containsMatch = _Book.title.contains(Pattern.contains("Java")); + *
*/ -public record Pattern(String value, boolean ignoreCase) { +public record Pattern(String pattern, boolean caseSensitive) { /** - * Creates a pattern for a {@link Operator#LIKE LIKE} match on the specified value. - * This method sets the field to `null`, allowing it to be applied - * later to a specific attribute. + * Creates a pattern for an exact match with the specified literal. * - * @return a Pattern instance for a pattern match. + *

Example usage:

+ *
+     * Restriction exactMatch = _Book.title.is(Pattern.is("Java Guide"));
+     * 
+ * + * @param literal the exact text to match. + * @return a {@code Pattern} instance for an exact match. */ - public static Pattern like(String pattern) { - return new Pattern(pattern, false); + public static Pattern is(String literal) { + return new Pattern(escape(literal), true); } /** - * Creates a pattern for a `LIKE` match where values start with the specified prefix. + * Creates a pattern for a match based on the specified custom pattern. + * + *

Example usage:

+ *
+     * Restriction customPatternMatch = _Book.title.matches(Pattern.matches("Ja%_a"));
+     * 
* - * @param prefixPattern the prefix pattern to match at the beginning of the field's value. - * @return a Pattern instance for a prefix match. + * @param pattern the pattern to match. + * @return a {@code Pattern} instance for a custom match. */ - public static Pattern startsWith(String prefixPattern) { - return new Pattern(prefixPattern + "%"); + public static Pattern matches(String pattern) { + return new Pattern(pattern, true); } /** - * Creates a pattern for a `LIKE` match where values contain the specified substring. - * This method initializes the field to `null`, allowing the pattern to be applied to - * a specific attribute later. + * Creates a pattern using custom single and multi-character wildcards. + * Allows replacing placeholders in the pattern with standard SQL wildcards. + * + *

Example usage:

+ *
+     * Restriction wildcardMatch = _Book.title.matches(Pattern.matches("Ja?a%", '?', '*'));
+     * 
* - * @param value the substring to match within the field's value. - * @return a Pattern instance for a substring match. + * @param pattern the custom pattern to match. + * @param characterWildcard the character to use as a single-character wildcard. + * @param stringWildcard the character to use as a multi-character wildcard. + * @return a {@code Pattern} instance for a custom match with specified wildcards. */ - public static Pattern contains(String value) { - return new Pattern("%" + value + "%", false); + public static Pattern matches(String pattern, char characterWildcard, char stringWildcard) { + final String standardized = escape(pattern) + .replace(characterWildcard, '_') + .replace(stringWildcard, '%'); + return new Pattern(standardized, true); } /** - * Creates a pattern for a `LIKE` match where values end with the specified suffix. - * The field is set to `null`, allowing assignment to a specific attribute later. + * Creates a pattern for a match where values start with the specified prefix. * + *

Example usage:

+ *
+     * Restriction prefixMatch = _Book.title.startsWith(Pattern.startsWith("Hibernate"));
+     * 
* - * @param value the suffix to match at the end of the field's value. - * @return a Pattern instance for a suffix match. + * @param prefix the prefix to match at the beginning of the value. + * @return a {@code Pattern} instance for a prefix match. */ - public static Pattern endsWith(String value) { - return new Pattern(value + "%", false); + public static Pattern startsWith(String prefix) { + return new Pattern(escape(prefix) + '%', true); } /** - * Returns a new {@code Pattern} instance with case-insensitive matching. - * This method allows you to specify that the pattern should ignore case when matching. + * Creates a pattern for a match where values end with the specified suffix. * + *

Example usage:

*
-     * // Case-insensitive prefix match
-     * Restriction titlePattern = Pattern.startsWith("Hibernate").ignoringCase();
+     * Restriction suffixMatch = _Book.title.endsWith(Pattern.endsWith("Guide"));
      * 
* - * @return a new Pattern instance with `ignoreCase` set to `true`. + * @param suffix the suffix to match at the end of the value. + * @return a {@code Pattern} instance for a suffix match. */ - public Pattern ignoringCase() { - return new Pattern(value, true); + public static Pattern endsWith(String suffix) { + return new Pattern('%' + escape(suffix), true); + } + + /** + * Creates a pattern for a match where values contain the specified substring. + * + *

Example usage:

+ *
+     * Restriction substringMatch = _Book.title.contains(Pattern.contains("Java"));
+     * 
+ * + * @param substring the substring to match within the value. + * @return a {@code Pattern} instance for a substring match. + */ + public static Pattern contains(String substring) { + return new Pattern('%' + escape(substring) + '%', true); + } + + /** + * Escapes special characters in the pattern, such as underscores and percent signs, + * to ensure literal matches for these characters. + * + * @param literal the text to escape. + * @return the escaped text with special characters handled. + */ + private static String escape(String literal) { + return literal.replace("_", "\\_").replace("%", "\\%"); } } + From f0f551a1ddd78c3ea62b3cf7e84f6872f69b6428 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 07:28:26 +0000 Subject: [PATCH 44/57] feat: create pattern structure bases on gavin suggestion Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index 53fd819b5..640d200f2 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -34,7 +34,7 @@ * Restriction containsMatch = _Book.title.contains(Pattern.contains("Java")); * */ -public record Pattern(String pattern, boolean caseSensitive) { +public record Pattern(String value, boolean caseSensitive) { /** * Creates a pattern for an exact match with the specified literal. From da4cfdf7e83efa1f23e29221ac713643fb00d789 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 07:35:01 +0000 Subject: [PATCH 45/57] docs: update documentation in pattern Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Pattern.java | 36 ++++++++++----------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index 640d200f2..a9b5c851f 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -17,31 +17,29 @@ */ package jakarta.data; -import jakarta.data.metamodel.Restriction; - /** - * Represents a pattern-based restriction for matching operations, supporting options - * such as exact match, prefix, suffix, and substring matching. This implementation - * provides flexibility in creating pattern-based conditions directly as {@link Restriction} instances. + * Represents a pattern for use in string matching conditions. This class supports options + * such as exact match, prefix, suffix, and substring matching. It is intended to be used + * with attributes that apply the pattern to create a {@link jakarta.data.metamodel.Restriction}. * - *

Example usage with metadata attributes:

+ *

Example usage:

*
- * // Match for values starting with "Guide"
- * Restriction prefixMatch = _Book.title.startsWith("Guide");
+ * // Pattern for a case-sensitive prefix match for values starting with "Guide"
+ * Pattern prefixMatch = Pattern.startsWith("Guide");
  *
- * // Match for values containing "Java"
- * Restriction containsMatch = _Book.title.contains(Pattern.contains("Java"));
+ * // Pattern for matching values containing "Java"
+ * Pattern containsMatch = Pattern.contains("Java");
  * 
*/ -public record Pattern(String value, boolean caseSensitive) { +public record Pattern(String pattern, boolean caseSensitive) { /** * Creates a pattern for an exact match with the specified literal. * *

Example usage:

*
-     * Restriction exactMatch = _Book.title.is(Pattern.is("Java Guide"));
+     * Pattern exactMatch = Pattern.is("Java Guide");
      * 
* * @param literal the exact text to match. @@ -52,11 +50,11 @@ public static Pattern is(String literal) { } /** - * Creates a pattern for a match based on the specified custom pattern. + * Creates a pattern for a custom match. * *

Example usage:

*
-     * Restriction customPatternMatch = _Book.title.matches(Pattern.matches("Ja%_a"));
+     * Pattern customPatternMatch = Pattern.matches("Ja%_a");
      * 
* * @param pattern the pattern to match. @@ -68,11 +66,10 @@ public static Pattern matches(String pattern) { /** * Creates a pattern using custom single and multi-character wildcards. - * Allows replacing placeholders in the pattern with standard SQL wildcards. * *

Example usage:

*
-     * Restriction wildcardMatch = _Book.title.matches(Pattern.matches("Ja?a%", '?', '*'));
+     * Pattern wildcardMatch = Pattern.matches("Ja?a%", '?', '*');
      * 
* * @param pattern the custom pattern to match. @@ -92,7 +89,7 @@ public static Pattern matches(String pattern, char characterWildcard, char strin * *

Example usage:

*
-     * Restriction prefixMatch = _Book.title.startsWith(Pattern.startsWith("Hibernate"));
+     * Pattern prefixMatch = Pattern.startsWith("Hibernate");
      * 
* * @param prefix the prefix to match at the beginning of the value. @@ -107,7 +104,7 @@ public static Pattern startsWith(String prefix) { * *

Example usage:

*
-     * Restriction suffixMatch = _Book.title.endsWith(Pattern.endsWith("Guide"));
+     * Pattern suffixMatch = Pattern.endsWith("Guide");
      * 
* * @param suffix the suffix to match at the end of the value. @@ -122,7 +119,7 @@ public static Pattern endsWith(String suffix) { * *

Example usage:

*
-     * Restriction substringMatch = _Book.title.contains(Pattern.contains("Java"));
+     * Pattern substringMatch = Pattern.contains("Java");
      * 
* * @param substring the substring to match within the value. @@ -144,3 +141,4 @@ private static String escape(String literal) { } } + From a17d2eda3650ca9cdbbe47ddd8570992e3ac4430 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 11:40:55 +0000 Subject: [PATCH 46/57] feat: update pattern name Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Pattern.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index a9b5c851f..fc07ae746 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -32,7 +32,7 @@ * Pattern containsMatch = Pattern.contains("Java"); * */ -public record Pattern(String pattern, boolean caseSensitive) { +public record Pattern(String value, boolean caseSensitive) { /** * Creates a pattern for an exact match with the specified literal. From 6d2916b070f8471a09f257131379cc32056da503 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 11:49:37 +0000 Subject: [PATCH 47/57] feat: update module info Signed-off-by: Otavio Santana --- .../jakarta/data/metamodel/Attribute.java | 4 +- .../data/metamodel/CompositeRestriction.java | 53 ++++---------- .../metamodel/CompositeRestrictionRecord.java | 72 +++++++++++++++++++ ...ict.java => CompositeRestrictionType.java} | 4 +- .../data/metamodel/MultipleRestriction.java | 49 ------------- .../jakarta/data/metamodel/Restriction.java | 2 +- ...estriction.java => RestrictionRecord.java} | 4 +- .../data/metamodel/SortableAttribute.java | 8 +-- api/src/main/java/module-info.java | 1 + 9 files changed, 99 insertions(+), 98 deletions(-) create mode 100644 api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java rename api/src/main/java/jakarta/data/metamodel/{Restrict.java => CompositeRestrictionType.java} (91%) delete mode 100644 api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java rename api/src/main/java/jakarta/data/metamodel/{SimpleRestriction.java => RestrictionRecord.java} (91%) diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 487d20c64..e2bf35c26 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -41,7 +41,7 @@ public interface Attribute { * @return a Restriction representing an equality condition. */ default Restriction equal(Object value) { - return new SimpleRestriction<>(name(), Operator.EQUAL, value); + return new RestrictionRecord<>(name(), Operator.EQUAL, value); } /** @@ -50,6 +50,6 @@ default Restriction equal(Object value) { * @return a Restriction representing the condition where the attribute is null. */ default Restriction isNull(){ - return new SimpleRestriction<>(name(), Operator.EQUAL, null); + return new RestrictionRecord<>(name(), Operator.EQUAL, null); } } diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java index 7c34c9011..ed2f05dfb 100644 --- a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java @@ -17,56 +17,33 @@ */ package jakarta.data.metamodel; -import java.util.Iterator; import java.util.List; /** - * A composite restriction representing a collection of individual {@link Restriction} - * and {@link Restrict} instances, combined under a single logical operation. + * A composite restriction that combines multiple {@link Restriction} instances using logical operators. * - *

This record allows multiple restrictions to be treated as a single entity, making - * it easy to pass complex conditions to repository methods.

+ *

The {@code MultipleRestriction} interface allows for combining multiple restrictions, enabling complex + * filtering scenarios where multiple conditions must be satisfied. Each contained {@link Restriction} + * can be evaluated based on the logical operator specified by the {@link CompositeRestrictionType} type.

* - * @param the entity type that the restrictions apply to. + *

This interface is useful for defining AND/OR conditions where multiple fields and restrictions + * are evaluated together in a repository query.

+ * + * @param the type of the entity on which the restriction is applied. */ -public record CompositeRestriction(Restrict type, List> restrictions) implements Iterable>, MultipleRestriction { +public interface CompositeRestriction extends Restriction { /** - * Constructs a composite restriction with the specified operator and list of restrictions. + * The list of restrictions that are combined in this composite restriction. * - * @param restrictions the list of restrictions to combine. + * @return a list of individual restrictions. */ - public CompositeRestriction { - restrictions = List.copyOf(restrictions); // Ensure immutability of the list - } - - @Override - public Iterator> iterator() { - return restrictions.iterator(); - } + List> restrictions(); /** - * Creates a composite restriction where all specified restrictions must be true (AND logic). + * The logical operator used to combine the contained restrictions, such as AND or OR. * - * @param restrictions the individual restrictions to combine. - * @param the entity type that the restrictions apply to. - * @return a CompositeRestriction representing the AND combination of the provided restrictions. + * @return the logical combination type for this composite restriction. */ - @SafeVarargs - public static CompositeRestriction all(Restriction... restrictions) { - return new CompositeRestriction<>(Restrict.ALL, List.of(restrictions)); - } - - /** - * Creates a composite restriction where any of the specified restrictions may be true (OR logic). - * - * @param restrictions the individual restrictions to combine. - * @param the entity type that the restrictions apply to. - * @return a CompositeRestriction representing the OR combination of the provided restrictions. - */ - @SafeVarargs - public static CompositeRestriction any(Restriction... restrictions) { - return new CompositeRestriction<>(Restrict.ANY, List.of(restrictions)); - } - + CompositeRestrictionType type(); } diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java b/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java new file mode 100644 index 000000000..bc5f84650 --- /dev/null +++ b/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import java.util.Iterator; +import java.util.List; + +/** + * A composite restriction representing a collection of individual {@link Restriction} + * and {@link CompositeRestrictionType} instances, combined under a single logical operation. + * + *

This record allows multiple restrictions to be treated as a single entity, making + * it easy to pass complex conditions to repository methods.

+ * + * @param the entity type that the restrictions apply to. + */ +record CompositeRestrictionRecord(CompositeRestrictionType type, List> restrictions) implements Iterable>, CompositeRestriction { + + /** + * Constructs a composite restriction with the specified operator and list of restrictions. + * + * @param restrictions the list of restrictions to combine. + */ + public CompositeRestrictionRecord { + restrictions = List.copyOf(restrictions); // Ensure immutability of the list + } + + @Override + public Iterator> iterator() { + return restrictions.iterator(); + } + + /** + * Creates a composite restriction where all specified restrictions must be true (AND logic). + * + * @param restrictions the individual restrictions to combine. + * @param the entity type that the restrictions apply to. + * @return a CompositeRestriction representing the AND combination of the provided restrictions. + */ + @SafeVarargs + public static CompositeRestrictionRecord all(Restriction... restrictions) { + return new CompositeRestrictionRecord<>(CompositeRestrictionType.ALL, List.of(restrictions)); + } + + /** + * Creates a composite restriction where any of the specified restrictions may be true (OR logic). + * + * @param restrictions the individual restrictions to combine. + * @param the entity type that the restrictions apply to. + * @return a CompositeRestriction representing the OR combination of the provided restrictions. + */ + @SafeVarargs + public static CompositeRestrictionRecord any(Restriction... restrictions) { + return new CompositeRestrictionRecord<>(CompositeRestrictionType.ANY, List.of(restrictions)); + } + +} diff --git a/api/src/main/java/jakarta/data/metamodel/Restrict.java b/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java similarity index 91% rename from api/src/main/java/jakarta/data/metamodel/Restrict.java rename to api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java index ff1cfe2c6..d4cd99ee5 100644 --- a/api/src/main/java/jakarta/data/metamodel/Restrict.java +++ b/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java @@ -27,10 +27,10 @@ *
  • ANY - Requires that at least one of the contained restrictions is satisfied (logical OR).
  • * * - *

    This enum is typically used in {@link MultipleRestriction} to specify how its list of + *

    This enum is typically used in {@link CompositeRestriction} to specify how its list of * restrictions should be combined, allowing for flexible and complex query conditions.

    */ -public enum Restrict { +public enum CompositeRestrictionType { /** * Requires that all contained restrictions must be satisfied (logical AND). */ diff --git a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java b/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java deleted file mode 100644 index 0c818ccae..000000000 --- a/api/src/main/java/jakarta/data/metamodel/MultipleRestriction.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package jakarta.data.metamodel; - -import java.util.List; - -/** - * A composite restriction that combines multiple {@link Restriction} instances using logical operators. - * - *

    The {@code MultipleRestriction} interface allows for combining multiple restrictions, enabling complex - * filtering scenarios where multiple conditions must be satisfied. Each contained {@link Restriction} - * can be evaluated based on the logical operator specified by the {@link Restrict} type.

    - * - *

    This interface is useful for defining AND/OR conditions where multiple fields and restrictions - * are evaluated together in a repository query.

    - * - * @param the type of the entity on which the restriction is applied. - */ -public interface MultipleRestriction extends Restriction { - - /** - * The list of restrictions that are combined in this composite restriction. - * - * @return a list of individual restrictions. - */ - List> restrictions(); - - /** - * The logical operator used to combine the contained restrictions, such as AND or OR. - * - * @return the logical combination type for this composite restriction. - */ - Restrict type(); -} diff --git a/api/src/main/java/jakarta/data/metamodel/Restriction.java b/api/src/main/java/jakarta/data/metamodel/Restriction.java index 9a6551da8..2b26677f9 100644 --- a/api/src/main/java/jakarta/data/metamodel/Restriction.java +++ b/api/src/main/java/jakarta/data/metamodel/Restriction.java @@ -27,7 +27,7 @@ * flexible and type-safe filtering logic in repository queries.

    * *

    Subtypes include {@link BasicRestriction}, which handles single-field conditions, - * and {@link MultipleRestriction}, which combines multiple restrictions + * and {@link CompositeRestriction}, which combines multiple restrictions * using logical operators.

    * * @param the type of the entity on which the restriction is applied. diff --git a/api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java b/api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java similarity index 91% rename from api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java rename to api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java index fb2f10607..240107427 100644 --- a/api/src/main/java/jakarta/data/metamodel/SimpleRestriction.java +++ b/api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java @@ -25,7 +25,7 @@ * * @param the type of the entity on which the restriction is applied. */ -public record SimpleRestriction(String field, Operator operator, Object value) implements Restriction { +record RestrictionRecord(String field, Operator operator, Object value) implements Restriction { /** * Constructs a `BasicRestriction` with the specified field, operator, and value. @@ -34,7 +34,7 @@ public record SimpleRestriction(String field, Operator operator, Object value * @param operator the operator defining the comparison or condition. * @param value the value to compare the field against (optional for null checks). */ - public SimpleRestriction { + public RestrictionRecord { if (field == null || operator == null) { throw new IllegalArgumentException("Field and operator must not be null."); } diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index ba3b770ba..45dd9836d 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -57,7 +57,7 @@ public interface SortableAttribute extends Attribute { * @return a Restriction representing a greater-than condition. */ default Restriction greaterThan(Object value) { - return new SimpleRestriction<>(name(), Operator.GREATER_THAN, value); + return new RestrictionRecord<>(name(), Operator.GREATER_THAN, value); } /** @@ -67,7 +67,7 @@ default Restriction greaterThan(Object value) { * @return a Restriction representing a greater-than-or-equal condition. */ default Restriction greaterThanOrEqual(Object value) { - return new SimpleRestriction<>(name(), Operator.GREATER_THAN_EQUAL, value); + return new RestrictionRecord<>(name(), Operator.GREATER_THAN_EQUAL, value); } /** @@ -77,7 +77,7 @@ default Restriction greaterThanOrEqual(Object value) { * @return a Restriction representing a less-than condition. */ default Restriction lessThan(Object value) { - return new SimpleRestriction<>(name(), Operator.LESS_THAN, value); + return new RestrictionRecord<>(name(), Operator.LESS_THAN, value); } /** @@ -87,7 +87,7 @@ default Restriction lessThan(Object value) { * @return a Restriction representing a less-than-or-equal condition. */ default Restriction lessThanOrEqual(Object value) { - return new SimpleRestriction<>(name(), Operator.LESS_THAN_EQUAL, value); + return new RestrictionRecord<>(name(), Operator.LESS_THAN_EQUAL, value); } /** diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index f82c60db7..b73aacb8a 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -1077,4 +1077,5 @@ exports jakarta.data.spi; opens jakarta.data.metamodel; opens jakarta.data; + opens jakarta.data.metamodel.impl; } From ca058c45c0265011641cea7d67d7692a06142a87 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 11:59:02 +0000 Subject: [PATCH 48/57] feat: update structure from PR 895 Signed-off-by: Otavio Santana --- .../{metamodel => }/BasicRestriction.java | 4 +- .../{metamodel => }/CompositeRestriction.java | 2 +- .../CompositeRestrictionRecord.java | 2 +- .../CompositeRestrictionType.java | 2 +- api/src/main/java/jakarta/data/Pattern.java | 2 +- api/src/main/java/jakarta/data/Restrict.java | 44 +++++++++++++++++++ .../data/{metamodel => }/Restriction.java | 2 +- .../{metamodel => }/RestrictionRecord.java | 6 +-- .../jakarta/data/metamodel/Attribute.java | 2 + .../java/jakarta/data/metamodel/Range.java | 4 +- .../data/metamodel/SortableAttribute.java | 13 +++--- .../jakarta/data/metamodel/TextAttribute.java | 1 + .../impl/BasicRestrictionRecord.java | 2 +- api/src/main/java/module-info.java | 1 + 14 files changed, 67 insertions(+), 20 deletions(-) rename api/src/main/java/jakarta/data/{metamodel => }/BasicRestriction.java (96%) rename api/src/main/java/jakarta/data/{metamodel => }/CompositeRestriction.java (98%) rename api/src/main/java/jakarta/data/{metamodel => }/CompositeRestrictionRecord.java (98%) rename api/src/main/java/jakarta/data/{metamodel => }/CompositeRestrictionType.java (97%) create mode 100644 api/src/main/java/jakarta/data/Restrict.java rename api/src/main/java/jakarta/data/{metamodel => }/Restriction.java (97%) rename api/src/main/java/jakarta/data/{metamodel => }/RestrictionRecord.java (93%) diff --git a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java b/api/src/main/java/jakarta/data/BasicRestriction.java similarity index 96% rename from api/src/main/java/jakarta/data/metamodel/BasicRestriction.java rename to api/src/main/java/jakarta/data/BasicRestriction.java index bb2ac0671..a59ab7426 100644 --- a/api/src/main/java/jakarta/data/metamodel/BasicRestriction.java +++ b/api/src/main/java/jakarta/data/BasicRestriction.java @@ -15,9 +15,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.metamodel; - -import jakarta.data.Operator; +package jakarta.data; /** * A basic restriction applies to a single field, representing conditions such as equality, diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java b/api/src/main/java/jakarta/data/CompositeRestriction.java similarity index 98% rename from api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java rename to api/src/main/java/jakarta/data/CompositeRestriction.java index ed2f05dfb..d829b62f2 100644 --- a/api/src/main/java/jakarta/data/metamodel/CompositeRestriction.java +++ b/api/src/main/java/jakarta/data/CompositeRestriction.java @@ -15,7 +15,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.metamodel; +package jakarta.data; import java.util.List; diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java b/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java similarity index 98% rename from api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java rename to api/src/main/java/jakarta/data/CompositeRestrictionRecord.java index bc5f84650..01d453c1a 100644 --- a/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionRecord.java +++ b/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java @@ -15,7 +15,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.metamodel; +package jakarta.data; import java.util.Iterator; import java.util.List; diff --git a/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java b/api/src/main/java/jakarta/data/CompositeRestrictionType.java similarity index 97% rename from api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java rename to api/src/main/java/jakarta/data/CompositeRestrictionType.java index d4cd99ee5..e96b2ded4 100644 --- a/api/src/main/java/jakarta/data/metamodel/CompositeRestrictionType.java +++ b/api/src/main/java/jakarta/data/CompositeRestrictionType.java @@ -15,7 +15,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package jakarta.data.metamodel; +package jakarta.data; /** diff --git a/api/src/main/java/jakarta/data/Pattern.java b/api/src/main/java/jakarta/data/Pattern.java index fc07ae746..71487feb7 100644 --- a/api/src/main/java/jakarta/data/Pattern.java +++ b/api/src/main/java/jakarta/data/Pattern.java @@ -21,7 +21,7 @@ /** * Represents a pattern for use in string matching conditions. This class supports options * such as exact match, prefix, suffix, and substring matching. It is intended to be used - * with attributes that apply the pattern to create a {@link jakarta.data.metamodel.Restriction}. + * with attributes that apply the pattern to create a {@link Restriction}. * *

    Example usage:

    *
    diff --git a/api/src/main/java/jakarta/data/Restrict.java b/api/src/main/java/jakarta/data/Restrict.java
    new file mode 100644
    index 000000000..a07b18155
    --- /dev/null
    +++ b/api/src/main/java/jakarta/data/Restrict.java
    @@ -0,0 +1,44 @@
    +package jakarta.data;
    +
    +import java.util.List;
    +import java.util.Set;
    +
    +public class Restrict {
    +
    +
    +    @SafeVarargs
    +    public static  Restriction all(Restriction... restrictions) {
    +        return new CompositeRestrictionRecord<>(CompositeRestrictionType.ALL, List.of(restrictions));
    +    }
    +
    +    @SafeVarargs
    +    public static  Restriction any(Restriction... restrictions) {
    +        return new CompositeRestrictionRecord<>(CompositeRestrictionType.ANY, List.of(restrictions));
    +    }
    +
    +    public static  Restriction equalTo(Object value, String field) {
    +        return new RestrictionRecord<>(field, Operator.EQUAL, value);
    +    }
    +
    +
    +    public static  Restriction lessThan(Object value, String field) {
    +        return new RestrictionRecord<>(field, Operator.LESS_THAN, value);
    +    }
    +
    +    public static  Restriction greaterThanEqual(Object value, String field) {
    +        return new RestrictionRecord<>(field, Operator.GREATER_THAN_EQUAL, value);
    +    }
    +
    +    public static  Restriction lessThanEqual(Object value, String field) {
    +        return new RestrictionRecord<>(field, Operator.LESS_THAN_EQUAL, value);
    +    }
    +
    +    public static  Restriction greaterThan(Object value, String field) {
    +        return new RestrictionRecord<>(field, Operator.GREATER_THAN, value);
    +    }
    +
    +    public static  Restriction in(Set values, String field) {
    +        return new RestrictionRecord<>(field, Operator.IN, values);
    +    }
    +
    +}
    diff --git a/api/src/main/java/jakarta/data/metamodel/Restriction.java b/api/src/main/java/jakarta/data/Restriction.java
    similarity index 97%
    rename from api/src/main/java/jakarta/data/metamodel/Restriction.java
    rename to api/src/main/java/jakarta/data/Restriction.java
    index 2b26677f9..cdb962178 100644
    --- a/api/src/main/java/jakarta/data/metamodel/Restriction.java
    +++ b/api/src/main/java/jakarta/data/Restriction.java
    @@ -15,7 +15,7 @@
      *
      * SPDX-License-Identifier: Apache-2.0
      */
    -package jakarta.data.metamodel;
    +package jakarta.data;
     
     
     /**
    diff --git a/api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java b/api/src/main/java/jakarta/data/RestrictionRecord.java
    similarity index 93%
    rename from api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java
    rename to api/src/main/java/jakarta/data/RestrictionRecord.java
    index 240107427..be1a1335e 100644
    --- a/api/src/main/java/jakarta/data/metamodel/RestrictionRecord.java
    +++ b/api/src/main/java/jakarta/data/RestrictionRecord.java
    @@ -15,9 +15,7 @@
      *
      * SPDX-License-Identifier: Apache-2.0
      */
    -package jakarta.data.metamodel;
    -
    -import jakarta.data.Operator;
    +package jakarta.data;
     
     /**
      * A basic implementation of the `Restriction` interface, representing various conditions
    @@ -25,7 +23,7 @@
      *
      * @param  the type of the entity on which the restriction is applied.
      */
    -record RestrictionRecord(String field, Operator operator, Object value) implements Restriction {
    +record RestrictionRecord(String field, Operator operator, Object value) implements BasicRestriction {
     
         /**
          * Constructs a `BasicRestriction` with the specified field, operator, and value.
    diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java
    index e2bf35c26..3e865cddc 100644
    --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java
    +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java
    @@ -18,6 +18,8 @@
     package jakarta.data.metamodel;
     
     import jakarta.data.Operator;
    +import jakarta.data.Restriction;
    +import jakarta.data.RestrictionRecord;
     import jakarta.data.Sort;
     
     /**
    diff --git a/api/src/main/java/jakarta/data/metamodel/Range.java b/api/src/main/java/jakarta/data/metamodel/Range.java
    index b13cd6b8d..a7577c4f1 100644
    --- a/api/src/main/java/jakarta/data/metamodel/Range.java
    +++ b/api/src/main/java/jakarta/data/metamodel/Range.java
    @@ -17,7 +17,9 @@
      */
     package jakarta.data.metamodel;
     
    +import jakarta.data.BasicRestriction;
     import jakarta.data.Operator;
    +import jakarta.data.Restriction;
     
     import java.util.List;
     
    @@ -25,7 +27,7 @@
      * Represents a range-based restriction for repository queries, allowing comparisons within
      * a specified range or boundary on a particular entity attribute.
      *
    - * 

    This class implements {@link Restriction} and provides flexible factory methods + *

    This class implements {@link Restriction } and provides flexible factory methods * to create range-based conditions, supporting inclusive and exclusive bounds.

    * *

    Usage examples:

    diff --git a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java index 45dd9836d..bd1a64ed0 100644 --- a/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/SortableAttribute.java @@ -17,7 +17,8 @@ */ package jakarta.data.metamodel; -import jakarta.data.Operator; +import jakarta.data.Restrict; +import jakarta.data.Restriction; import jakarta.data.Sort; /** @@ -57,7 +58,7 @@ public interface SortableAttribute extends Attribute { * @return a Restriction representing a greater-than condition. */ default Restriction greaterThan(Object value) { - return new RestrictionRecord<>(name(), Operator.GREATER_THAN, value); + return Restrict.greaterThan(value, name()); } /** @@ -66,8 +67,8 @@ default Restriction greaterThan(Object value) { * @param value the lower bound (inclusive) for the attribute. * @return a Restriction representing a greater-than-or-equal condition. */ - default Restriction greaterThanOrEqual(Object value) { - return new RestrictionRecord<>(name(), Operator.GREATER_THAN_EQUAL, value); + default Restriction greaterThanEqual(Object value) { + return Restrict.greaterThanEqual(value, name()); } /** @@ -77,7 +78,7 @@ default Restriction greaterThanOrEqual(Object value) { * @return a Restriction representing a less-than condition. */ default Restriction lessThan(Object value) { - return new RestrictionRecord<>(name(), Operator.LESS_THAN, value); + return Restrict.lessThan(value, name()); } /** @@ -87,7 +88,7 @@ default Restriction lessThan(Object value) { * @return a Restriction representing a less-than-or-equal condition. */ default Restriction lessThanOrEqual(Object value) { - return new RestrictionRecord<>(name(), Operator.LESS_THAN_EQUAL, value); + return Restrict.lessThanEqual(value, name()); } /** diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 57b4f7114..0a1e80884 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -19,6 +19,7 @@ import jakarta.data.Operator; import jakarta.data.Pattern; +import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.impl.BasicRestrictionRecord; diff --git a/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java b/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java index 2c7b1987f..810d80343 100644 --- a/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java +++ b/api/src/main/java/jakarta/data/metamodel/impl/BasicRestrictionRecord.java @@ -18,7 +18,7 @@ package jakarta.data.metamodel.impl; import jakarta.data.Operator; -import jakarta.data.metamodel.BasicRestriction; +import jakarta.data.BasicRestriction; public record BasicRestrictionRecord(String field, Operator operator, Object value) implements BasicRestriction { diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index b73aacb8a..eec8ab95a 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -1066,6 +1066,7 @@ * of the repository operation.

    */ module jakarta.data { + requires java.management; exports jakarta.data; exports jakarta.data.metamodel; exports jakarta.data.metamodel.impl; From 13bd83dc96d16be233e2cae599697412a913dd84 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 11:59:34 +0000 Subject: [PATCH 49/57] feat: update to restrict Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Restrict.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/api/src/main/java/jakarta/data/Restrict.java b/api/src/main/java/jakarta/data/Restrict.java index a07b18155..0b81027e1 100644 --- a/api/src/main/java/jakarta/data/Restrict.java +++ b/api/src/main/java/jakarta/data/Restrict.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ package jakarta.data; import java.util.List; From d44817d9fa845be8bd1dceca0e4f302040bdf592 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 12:03:14 +0000 Subject: [PATCH 50/57] documentation: include restrict javadoc Signed-off-by: Otavio Santana --- .../data/CompositeRestrictionRecord.java | 3 +- api/src/main/java/jakarta/data/Restrict.java | 86 ++++++++++++++++++- .../jakarta/data/metamodel/Attribute.java | 7 +- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java b/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java index 01d453c1a..2cb9a7452 100644 --- a/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java +++ b/api/src/main/java/jakarta/data/CompositeRestrictionRecord.java @@ -29,7 +29,8 @@ * * @param the entity type that the restrictions apply to. */ -record CompositeRestrictionRecord(CompositeRestrictionType type, List> restrictions) implements Iterable>, CompositeRestriction { +record CompositeRestrictionRecord(CompositeRestrictionType type, List> restrictions) + implements Iterable>, CompositeRestriction { /** * Constructs a composite restriction with the specified operator and list of restrictions. diff --git a/api/src/main/java/jakarta/data/Restrict.java b/api/src/main/java/jakarta/data/Restrict.java index 0b81027e1..b895bd805 100644 --- a/api/src/main/java/jakarta/data/Restrict.java +++ b/api/src/main/java/jakarta/data/Restrict.java @@ -20,42 +20,122 @@ import java.util.List; import java.util.Set; -public class Restrict { +/** + * Utility class for constructing complex restrictions in repository queries. + * The `Restrict` class provides static methods for creating basic and composite + * restrictions, supporting conditions like equality, comparisons, and logical + * combinations (`ALL` and `ANY`). + * + *

    Example usage:

    + *
    + * // Create a single equality restriction
    + * Restriction titleRestriction = Restrict.equalTo("Java Guide", "title");
    + *
    + * // Create a composite restriction using AND logic
    + * Restriction compositeRestriction = Restrict.all(
    + *     Restrict.equalTo("Java Guide", "title"),
    + *     Restrict.greaterThan(2020, "publicationYear")
    + * );
    + * 
    + */ +public final class Restrict { + private Restrict() { + } + /** + * Creates a composite restriction that requires all specified restrictions to be met. + * + * @param restrictions the list of restrictions to combine with AND logic. + * @param the entity type. + * @return a composite restriction using AND logic. + */ @SafeVarargs public static Restriction all(Restriction... restrictions) { return new CompositeRestrictionRecord<>(CompositeRestrictionType.ALL, List.of(restrictions)); } + /** + * Creates a composite restriction that requires at least one of the specified restrictions to be met. + * + * @param restrictions the list of restrictions to combine with OR logic. + * @param the entity type. + * @return a composite restriction using OR logic. + */ @SafeVarargs public static Restriction any(Restriction... restrictions) { return new CompositeRestrictionRecord<>(CompositeRestrictionType.ANY, List.of(restrictions)); } + /** + * Creates an equality restriction for the specified field and value. + * + * @param value the value to match exactly. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return an equality restriction for the specified field. + */ public static Restriction equalTo(Object value, String field) { return new RestrictionRecord<>(field, Operator.EQUAL, value); } - + /** + * Creates a "less than" restriction for the specified field and value. + * + * @param value the upper bound (exclusive) for the field. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a "less than" restriction for the specified field. + */ public static Restriction lessThan(Object value, String field) { return new RestrictionRecord<>(field, Operator.LESS_THAN, value); } + /** + * Creates a "greater than or equal to" restriction for the specified field and value. + * + * @param value the lower bound (inclusive) for the field. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a "greater than or equal to" restriction for the specified field. + */ public static Restriction greaterThanEqual(Object value, String field) { return new RestrictionRecord<>(field, Operator.GREATER_THAN_EQUAL, value); } + /** + * Creates a "less than or equal to" restriction for the specified field and value. + * + * @param value the upper bound (inclusive) for the field. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a "less than or equal to" restriction for the specified field. + */ public static Restriction lessThanEqual(Object value, String field) { return new RestrictionRecord<>(field, Operator.LESS_THAN_EQUAL, value); } + /** + * Creates a "greater than" restriction for the specified field and value. + * + * @param value the lower bound (exclusive) for the field. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a "greater than" restriction for the specified field. + */ public static Restriction greaterThan(Object value, String field) { return new RestrictionRecord<>(field, Operator.GREATER_THAN, value); } + /** + * Creates an "in" restriction, restricting the field to match any value in the specified set. + * + * @param values the set of allowed values for the field. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return an "in" restriction for the specified field. + */ public static Restriction in(Set values, String field) { return new RestrictionRecord<>(field, Operator.IN, values); } - } diff --git a/api/src/main/java/jakarta/data/metamodel/Attribute.java b/api/src/main/java/jakarta/data/metamodel/Attribute.java index 3e865cddc..6cb3efd9a 100644 --- a/api/src/main/java/jakarta/data/metamodel/Attribute.java +++ b/api/src/main/java/jakarta/data/metamodel/Attribute.java @@ -17,9 +17,8 @@ */ package jakarta.data.metamodel; -import jakarta.data.Operator; +import jakarta.data.Restrict; import jakarta.data.Restriction; -import jakarta.data.RestrictionRecord; import jakarta.data.Sort; /** @@ -43,7 +42,7 @@ public interface Attribute { * @return a Restriction representing an equality condition. */ default Restriction equal(Object value) { - return new RestrictionRecord<>(name(), Operator.EQUAL, value); + return Restrict.equalTo(value, name()); } /** @@ -52,6 +51,6 @@ default Restriction equal(Object value) { * @return a Restriction representing the condition where the attribute is null. */ default Restriction isNull(){ - return new RestrictionRecord<>(name(), Operator.EQUAL, null); + return Restrict.equalTo(null, name()); } } From d973ec787428ca3f87fbf103118c125e5f900adf Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 13 Nov 2024 12:09:11 +0000 Subject: [PATCH 51/57] feat: create instances at TextAttribute using Restrict Signed-off-by: Otavio Santana --- api/src/main/java/jakarta/data/Restrict.java | 72 +++++++++++++++++++ .../jakarta/data/metamodel/TextAttribute.java | 10 ++- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/jakarta/data/Restrict.java b/api/src/main/java/jakarta/data/Restrict.java index b895bd805..217fb50f4 100644 --- a/api/src/main/java/jakarta/data/Restrict.java +++ b/api/src/main/java/jakarta/data/Restrict.java @@ -138,4 +138,76 @@ public static Restriction greaterThan(Object value, String field) { public static Restriction in(Set values, String field) { return new RestrictionRecord<>(field, Operator.IN, values); } + + /** + * Creates a restriction that matches field values containing the specified substring. + * + *

    Example usage:

    + *
    +     * Restriction containsMatch = Restrict.contains("Java", "title");
    +     * 
    + * + * @param value the substring to search for within the field's value. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a restriction that matches values containing the specified substring. + */ + public static Restriction contains(String value, String field) { + var contains = Pattern.contains(value); + return new RestrictionRecord<>(field, Operator.LIKE, contains.value()); + } + + /** + * Creates a restriction that matches field values starting with the specified prefix. + * + *

    Example usage:

    + *
    +     * Restriction startsWithMatch = Restrict.startsWith("Guide", "title");
    +     * 
    + * + * @param value the prefix to match at the beginning of the field's value. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a restriction that matches values starting with the specified prefix. + */ + public static Restriction startsWith(String value, String field) { + var contains = Pattern.startsWith(value); + return new RestrictionRecord<>(field, Operator.LIKE, contains.value()); + } + + /** + * Creates a restriction that matches field values ending with the specified suffix. + * + *

    Example usage:

    + *
    +     * Restriction endsWithMatch = Restrict.endsWith("Guide", "title");
    +     * 
    + * + * @param value the suffix to match at the end of the field's value. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a restriction that matches values ending with the specified suffix. + */ + public static Restriction endsWith(String value, String field) { + var contains = Pattern.endsWith(value); + return new RestrictionRecord<>(field, Operator.LIKE, contains.value()); + } + + /** + * Creates a restriction that matches field values ending with the specified pattern. + * + *

    Example usage:

    + *
    +     * Pattern pattern = Pattern.endsWith("Guide");
    +     * Restriction endsWithPatternMatch = Restrict.endsWith(pattern, "title");
    +     * 
    + * + * @param pattern the pattern to match at the end of the field's value. + * @param field the name of the field to apply the restriction on. + * @param the entity type. + * @return a restriction that matches values ending with the specified pattern. + */ + public static Restriction endsWith(Pattern pattern, String field) { + return new RestrictionRecord<>(field, Operator.LIKE, pattern.value()); + } } diff --git a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java index 0a1e80884..351ab5ccd 100644 --- a/api/src/main/java/jakarta/data/metamodel/TextAttribute.java +++ b/api/src/main/java/jakarta/data/metamodel/TextAttribute.java @@ -19,6 +19,7 @@ import jakarta.data.Operator; import jakarta.data.Pattern; +import jakarta.data.Restrict; import jakarta.data.Restriction; import jakarta.data.Sort; import jakarta.data.metamodel.impl.BasicRestrictionRecord; @@ -72,8 +73,7 @@ default Restriction like(Pattern pattern) { * @return a Restriction representing a prefix `LIKE` condition. */ default Restriction startsWith(String text) { - var pattern = Pattern.startsWith(text); - return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); + return Restrict.startsWith(text, name()); } /** @@ -83,8 +83,7 @@ default Restriction startsWith(String text) { * @return a Restriction representing a substring `LIKE` condition. */ default Restriction contains(String text) { - var pattern = Pattern.contains(text); - return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); + return Restrict.contains(text, name()); } @@ -95,8 +94,7 @@ default Restriction contains(String text) { * @return a Restriction representing a suffix `LIKE` condition. */ default Restriction endsWith(String text) { - var pattern = Pattern.endsWith(text); - return new BasicRestrictionRecord<>(name(), Operator.LIKE, pattern.value()); + return Restrict.endsWith(text, name()); } } From d8ada64bca78123c798c1201adc411718b0c3c10 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 19:51:50 +0000 Subject: [PATCH 52/57] test: create scenario cases to pattern Signed-off-by: Otavio Santana --- .../test/java/jakarta/data/PatternTest.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 api/src/test/java/jakarta/data/PatternTest.java diff --git a/api/src/test/java/jakarta/data/PatternTest.java b/api/src/test/java/jakarta/data/PatternTest.java new file mode 100644 index 000000000..1eba8222e --- /dev/null +++ b/api/src/test/java/jakarta/data/PatternTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class PatternTest { + + @Test + void shouldCreateExactMatchPattern() { + Pattern pattern = Pattern.is("Java Guide"); + assertThat(pattern.value()).isEqualTo("Java Guide"); + assertThat(pattern.caseSensitive()).isTrue(); + } + + @Test + void shouldCreateCustomMatchPattern() { + Pattern pattern = Pattern.matches("Ja%_a"); + assertThat(pattern.value()).isEqualTo("Ja%_a"); + assertThat(pattern.caseSensitive()).isTrue(); + } + + @Test + void shouldCreateCustomWildcardPattern() { + Pattern pattern = Pattern.matches("Ja?a*", '?', '*'); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(pattern.value()).isEqualTo("Ja_a%"); + soft.assertThat(pattern.caseSensitive()).isTrue(); + }); + } + + @Test + void shouldCreatePrefixMatchPattern() { + Pattern pattern = Pattern.startsWith("Hibernate"); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(pattern.value()).isEqualTo("Hibernate%"); + soft.assertThat(pattern.caseSensitive()).isTrue(); + }); + } + + @Test + void shouldCreateSuffixMatchPattern() { + Pattern pattern = Pattern.endsWith("Guide"); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(pattern.value()).isEqualTo("%Guide"); + soft.assertThat(pattern.caseSensitive()).isTrue(); + }); + } + + @Test + void shouldCreateSubstringMatchPattern() { + Pattern pattern = Pattern.contains("Java"); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(pattern.value()).isEqualTo("%Java%"); + soft.assertThat(pattern.caseSensitive()).isTrue(); + }); + } + + @ParameterizedTest + @CsvSource({ + "Jav_%,Jav\\_\\%", + "Hello%World,Hello\\%World", + "Special_Chars,Special\\_Chars" + }) + void shouldEscapeSpecialCharacters(String input, String expected) { + Pattern pattern = Pattern.is(input); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(pattern.value()).isEqualTo(expected); + soft.assertThat(pattern.caseSensitive()).isTrue(); + }); + } +} From 0897e47d5af765c99bf9cf032ae3dfa1c9f70786 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 19:54:33 +0000 Subject: [PATCH 53/57] test: create test to CompositeRestriction Signed-off-by: Otavio Santana --- .../data/CompositeRestrictionRecordTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java diff --git a/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java b/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java new file mode 100644 index 000000000..06ffa7adb --- /dev/null +++ b/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + + +class CompositeRestrictionRecordTest { + + @Test + void shouldCreateAllCompositeRestriction() { + Restriction restriction1 = Restrict.equalTo("value1", "field1"); + Restriction restriction2 = Restrict.lessThan(10, "field2"); + + CompositeRestrictionRecord composite = CompositeRestrictionRecord.all(restriction1, restriction2); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ALL); + soft.assertThat(composite.restrictions()).containsExactly(restriction1, restriction2); + }); + } + + @Test + void shouldCreateAnyCompositeRestriction() { + Restriction restriction1 = Restrict.greaterThan(5, "field1"); + Restriction restriction2 = Restrict.in(Set.of(1, 2, 3), "field2"); + + CompositeRestrictionRecord composite = CompositeRestrictionRecord.any(restriction1, restriction2); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ANY); + soft.assertThat(composite.restrictions()).containsExactly(restriction1, restriction2); + }); + } + + @Test + void shouldBeIterable() { + Restriction restriction1 = Restrict.equalTo("value1", "field1"); + Restriction restriction2 = Restrict.lessThan(10, "field2"); + + CompositeRestrictionRecord composite = CompositeRestrictionRecord.all(restriction1, restriction2); + + assertThat(composite).containsExactly(restriction1, restriction2); + } + + @Test + void shouldReturnImmutableRestrictionsList() { + Restriction restriction1 = Restrict.equalTo("value1", "field1"); + Restriction restriction2 = Restrict.lessThan(10, "field2"); + + CompositeRestrictionRecord composite = CompositeRestrictionRecord.all(restriction1, restriction2); + + assertThatThrownBy(() -> composite.restrictions().add(Restrict.greaterThan(15, "field3"))) + .isInstanceOf(UnsupportedOperationException.class); + } + + @Test + void shouldSupportEmptyRestrictions() { + CompositeRestrictionRecord composite = CompositeRestrictionRecord.all(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ALL); + soft.assertThat(composite.restrictions()).isEmpty(); + }); + } + + @Test + void shouldCombineDifferentRestrictionTypes() { + Restriction restriction1 = Restrict.contains("text", "field1"); + Restriction restriction2 = Restrict.startsWith("prefix", "field2"); + Restriction restriction3 = Restrict.endsWith("suffix", "field3"); + + CompositeRestrictionRecord composite = CompositeRestrictionRecord.all(restriction1, restriction2, restriction3); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ALL); + soft.assertThat(composite.restrictions()).containsExactly(restriction1, restriction2, restriction3); + }); + } +} From 3c64b07c8b0d27c8dfa8351bc7b3d492dab2eda8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 19:54:46 +0000 Subject: [PATCH 54/57] feat: remove unsed imports Signed-off-by: Otavio Santana --- .../test/java/jakarta/data/CompositeRestrictionRecordTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java b/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java index 06ffa7adb..55b020e69 100644 --- a/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java +++ b/api/src/test/java/jakarta/data/CompositeRestrictionRecordTest.java @@ -21,7 +21,6 @@ import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; -import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; From 216877acf21d866c3e82051b8f7ef97a5069fc17 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 20:01:17 +0000 Subject: [PATCH 55/57] test: create basic restrictionr record Signed-off-by: Otavio Santana --- .../impl/BasicRestrictionRecordTest.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 api/src/test/java/jakarta/data/metamodel/impl/BasicRestrictionRecordTest.java diff --git a/api/src/test/java/jakarta/data/metamodel/impl/BasicRestrictionRecordTest.java b/api/src/test/java/jakarta/data/metamodel/impl/BasicRestrictionRecordTest.java new file mode 100644 index 000000000..b151341f4 --- /dev/null +++ b/api/src/test/java/jakarta/data/metamodel/impl/BasicRestrictionRecordTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel.impl; + +import jakarta.data.Operator; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + + +class BasicRestrictionRecordTest { + @Test + void shouldCreateBasicEqualityRestriction() { + BasicRestrictionRecord restriction = new BasicRestrictionRecord<>("title", Operator.EQUAL, "Java Guide"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(restriction.field()).isEqualTo("title"); + soft.assertThat(restriction.operator()).isEqualTo(Operator.EQUAL); + soft.assertThat(restriction.value()).isEqualTo("Java Guide"); + }); + } + + @Test + void shouldCreateGreaterThanRestriction() { + BasicRestrictionRecord restriction = new BasicRestrictionRecord<>("price", Operator.GREATER_THAN, 100); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(restriction.field()).isEqualTo("price"); + soft.assertThat(restriction.operator()).isEqualTo(Operator.GREATER_THAN); + soft.assertThat(restriction.value()).isEqualTo(100); + }); + } + + @Test + void shouldCreateLessThanRestriction() { + BasicRestrictionRecord restriction = new BasicRestrictionRecord<>("quantity", Operator.LESS_THAN, 50); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(restriction.field()).isEqualTo("quantity"); + soft.assertThat(restriction.operator()).isEqualTo(Operator.LESS_THAN); + soft.assertThat(restriction.value()).isEqualTo(50); + }); + } + +} From 5515485d2127d5c3e1b5f7186443d742078df1eb Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 20:07:23 +0000 Subject: [PATCH 56/57] test: create restrict test Signed-off-by: Otavio Santana --- .../test/java/jakarta/data/RestrictTest.java | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 api/src/test/java/jakarta/data/RestrictTest.java diff --git a/api/src/test/java/jakarta/data/RestrictTest.java b/api/src/test/java/jakarta/data/RestrictTest.java new file mode 100644 index 000000000..0d02a13aa --- /dev/null +++ b/api/src/test/java/jakarta/data/RestrictTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class RestrictTest { + + @Test + void shouldCreateBasicEqualityRestriction() { + Restriction restriction = Restrict.equalTo("Java Guide", "title"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("title"); + soft.assertThat(basic.operator()).isEqualTo(Operator.EQUAL); + soft.assertThat(basic.value()).isEqualTo("Java Guide"); + }); + } + + @Test + void shouldCreateLessThanRestriction() { + Restriction restriction = Restrict.lessThan(100, "price"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("price"); + soft.assertThat(basic.operator()).isEqualTo(Operator.LESS_THAN); + soft.assertThat(basic.value()).isEqualTo(100); + }); + } + + @Test + void shouldCreateGreaterThanRestriction() { + Restriction restriction = Restrict.greaterThan(2020, "year"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("year"); + soft.assertThat(basic.operator()).isEqualTo(Operator.GREATER_THAN); + soft.assertThat(basic.value()).isEqualTo(2020); + }); + } + + @Test + void shouldCreateInRestriction() { + Restriction restriction = Restrict.in(Set.of("Java", "Spring"), "title"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("title"); + soft.assertThat(basic.operator()).isEqualTo(Operator.IN); + soft.assertThat(basic.value()).isInstanceOf(Set.class); + Set values = (Set) basic.value(); + soft.assertThat(values).containsExactlyInAnyOrder("Java", "Spring"); + }); + } + + @Test + void shouldCreateContainsRestriction() { + Restriction restriction = Restrict.contains("Hibernate", "title"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("title"); + soft.assertThat(basic.operator()).isEqualTo(Operator.LIKE); + soft.assertThat(basic.value()).isEqualTo("%Hibernate%"); + }); + } + + @Test + void shouldCreateStartsWithRestriction() { + Restriction restriction = Restrict.startsWith("Hibernate", "title"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("title"); + soft.assertThat(basic.operator()).isEqualTo(Operator.LIKE); + soft.assertThat(basic.value()).isEqualTo("Hibernate%"); + }); + } + + @Test + void shouldCreateEndsWithRestriction() { + Restriction restriction = Restrict.endsWith("Guide", "title"); + + assertThat(restriction).isInstanceOf(BasicRestriction.class); + BasicRestriction basic = (BasicRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(basic.field()).isEqualTo("title"); + soft.assertThat(basic.operator()).isEqualTo(Operator.LIKE); + soft.assertThat(basic.value()).isEqualTo("%Guide"); + }); + } + + @Test + void shouldCreateCompositeAllRestriction() { + Restriction restriction = Restrict.all( + Restrict.equalTo("Java Guide", "title"), + Restrict.greaterThan(2020, "publicationYear") + ); + + assertThat(restriction).isInstanceOf(CompositeRestriction.class); + CompositeRestriction composite = (CompositeRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ALL); + soft.assertThat(composite.restrictions()).hasSize(2); + soft.assertThat(composite.restrictions().get(0)).isInstanceOf(BasicRestriction.class); + soft.assertThat(composite.restrictions().get(1)).isInstanceOf(BasicRestriction.class); + }); + } + + @Test + void shouldCreateCompositeAnyRestriction() { + Restriction restriction = Restrict.any( + Restrict.contains("Java", "title"), + Restrict.lessThan(500, "pages") + ); + + assertThat(restriction).isInstanceOf(CompositeRestriction.class); + CompositeRestriction composite = (CompositeRestriction) restriction; + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(composite.type()).isEqualTo(CompositeRestrictionType.ANY); + soft.assertThat(composite.restrictions()).hasSize(2); + soft.assertThat(composite.restrictions().get(0)).isInstanceOf(BasicRestriction.class); + soft.assertThat(composite.restrictions().get(1)).isInstanceOf(BasicRestriction.class); + }); + } +} From f0b8a5b0ef88b4a305343983b76a5066b651f907 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sun, 17 Nov 2024 20:10:21 +0000 Subject: [PATCH 57/57] test: create test scenarios to Range Signed-off-by: Otavio Santana --- .../jakarta/data/metamodel/RangeTest.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 api/src/test/java/jakarta/data/metamodel/RangeTest.java diff --git a/api/src/test/java/jakarta/data/metamodel/RangeTest.java b/api/src/test/java/jakarta/data/metamodel/RangeTest.java new file mode 100644 index 000000000..8de44cc8f --- /dev/null +++ b/api/src/test/java/jakarta/data/metamodel/RangeTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.metamodel; + +import jakarta.data.Operator; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class RangeTest { + + + @Test + void shouldCreateInclusiveBetweenRange() { + Range range = Range.between("publicationDate", LocalDate.of(2020, 1, 1), LocalDate.of(2023, 1, 1)); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("publicationDate"); + soft.assertThat(range.operator()).isEqualTo(Operator.BETWEEN); + soft.assertThat(range.value()).isEqualTo(List.of(LocalDate.of(2020, 1, 1), LocalDate.of(2023, 1, 1))); + }); + } + + @Test + void shouldCreateInclusiveFromRange() { + Range range = Range.from("rating", 4.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("rating"); + soft.assertThat(range.operator()).isEqualTo(Operator.GREATER_THAN_EQUAL); + soft.assertThat(range.value()).isEqualTo(4.0); + }); + } + + @Test + void shouldCreateInclusiveToRange() { + Range range = Range.to("price", 100); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("price"); + soft.assertThat(range.operator()).isEqualTo(Operator.LESS_THAN_EQUAL); + soft.assertThat(range.value()).isEqualTo(100); + }); + } + + @Test + void shouldCreateExclusiveAboveRange() { + Range range = Range.above("rating", 4.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("rating"); + soft.assertThat(range.operator()).isEqualTo(Operator.GREATER_THAN); + soft.assertThat(range.value()).isEqualTo(4.0); + }); + } + + @Test + void shouldCreateExclusiveBelowRange() { + Range range = Range.below("price", 200.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("price"); + soft.assertThat(range.operator()).isEqualTo(Operator.LESS_THAN); + soft.assertThat(range.value()).isEqualTo(200.0); + }); + } + + @Test + void shouldHandleNullLowerBoundForToRange() { + Range range = Range.to("price", 200.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("price"); + soft.assertThat(range.operator()).isEqualTo(Operator.LESS_THAN_EQUAL); + soft.assertThat(range.lowerBound()).isNull(); + soft.assertThat(range.upperBound()).isEqualTo(200.0); + }); + } + + @Test + void shouldHandleNullUpperBoundForFromRange() { + Range range = Range.from("price", 50.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("price"); + soft.assertThat(range.operator()).isEqualTo(Operator.GREATER_THAN_EQUAL); + soft.assertThat(range.lowerBound()).isEqualTo(50.0); + soft.assertThat(range.upperBound()).isNull(); + }); + } + + @Test + void shouldHandleBothBoundsForBetweenRange() { + Range range = Range.between("price", 50.0, 200.0); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("price"); + soft.assertThat(range.operator()).isEqualTo(Operator.BETWEEN); + soft.assertThat(range.lowerBound()).isEqualTo(50.0); + soft.assertThat(range.upperBound()).isEqualTo(200.0); + }); + } + + @Test + void shouldHandleOpenRangeForAbove() { + Range range = Range.above("age", 18); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("age"); + soft.assertThat(range.operator()).isEqualTo(Operator.GREATER_THAN); + soft.assertThat(range.lowerBound()).isEqualTo(18); + soft.assertThat(range.upperBound()).isNull(); + soft.assertThat(range.open()).isTrue(); + }); + } + + @Test + void shouldHandleOpenRangeForBelow() { + Range range = Range.below("age", 65); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(range.field()).isEqualTo("age"); + soft.assertThat(range.operator()).isEqualTo(Operator.LESS_THAN); + soft.assertThat(range.lowerBound()).isNull(); + soft.assertThat(range.upperBound()).isEqualTo(65); + soft.assertThat(range.open()).isTrue(); + }); + } +}